1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673 |
- #if UNITY_2017_3_OR_NEWER
- #define AVPRO_MOVIECAPTURE_OFFLINE_AUDIOCAPTURE
- #endif
- #if UNITY_5_6_OR_NEWER && UNITY_2018_3_OR_NEWER
- #define AVPRO_MOVIECAPTURE_VIDEOPLAYER_SUPPORT
- #endif
- #if UNITY_2017_1_OR_NEWER
- #define AVPRO_MOVIECAPTURE_PLAYABLES_SUPPORT
- #endif
- #if UNITY_2019_2_OR_NEWER
- #define AVPRO_MOVIECAPTURE_CAPTUREDELTA_SUPPORT
- #endif
- #if UNITY_2018_1_OR_NEWER
- #define UNITY_NATIVEARRAY_UNSAFE_SUPPORT
- #endif
- #if ENABLE_IL2CPP
- using AOT;
- #endif
- #if UNITY_2018_1_OR_NEWER
- using Unity.Collections;
- #else
- using UnityEngine.Collections;
- #endif
- using UnityEngine;
- using System.IO;
- using System;
- using System.Collections.Generic;
- using UnityEngine.Rendering;
- using System.Runtime.InteropServices;
- #if UNITY_ANDROID && !UNITY_EDITOR
- using UnityEngine.Android;
- #endif
- //-----------------------------------------------------------------------------
- // Copyright 2012-2022 RenderHeads Ltd. All rights reserved.
- //-----------------------------------------------------------------------------
- namespace RenderHeads.Media.AVProMovieCapture
- {
- /// <summary>
- /// Live stats about an active capture session
- /// </summary>
- public class CaptureStats
- {
- public float FPS { get { return _fps; } }
- public float FramesTotal { get { return _frameTotal; } }
- public uint NumDroppedFrames { get { return _numDroppedFrames; } internal set { _numDroppedFrames = value; } }
- public uint NumDroppedEncoderFrames { get { return _numDroppedEncoderFrames; } internal set { _numDroppedEncoderFrames = value; } }
- public uint NumEncodedFrames { get { return _numEncodedFrames; } internal set { _numEncodedFrames = value; } }
- public uint TotalEncodedSeconds { get { return _totalEncodedSeconds; } internal set { _totalEncodedSeconds = value; } }
- public AudioCaptureSource AudioCaptureSource { get { return _audioCaptureSource; } internal set { _audioCaptureSource = value; } }
- public int UnityAudioSampleRate { get { return _unityAudioSampleRate; } internal set { _unityAudioSampleRate = value; } }
- public int UnityAudioChannelCount { get { return _unityAudioChannelCount; } internal set { _unityAudioChannelCount = value; } }
- // Frame stats
- private uint _numDroppedFrames = 0;
- private uint _numDroppedEncoderFrames = 0;
- private uint _numEncodedFrames = 0;
- private uint _totalEncodedSeconds = 0;
- // Audio
- private AudioCaptureSource _audioCaptureSource = AudioCaptureSource.None;
- private int _unityAudioSampleRate = -1;
- private int _unityAudioChannelCount = -1;
- // Capture rate
- private float _fps = 0f;
- private int _frameTotal = 0;
- private int _frameCount = 0;
- private float _startFrameTime = 0f;
- internal void ResetFPS()
- {
- _frameCount = 0;
- _frameTotal = 0;
- _fps = 0.0f;
- _startFrameTime = 0.0f;
- }
- internal void UpdateFPS()
- {
- _frameCount++;
- _frameTotal++;
- float timeNow = Time.realtimeSinceStartup;
- float timeDelta = timeNow - _startFrameTime;
- if (timeDelta >= 1.0f)
- {
- _fps = (float)_frameCount / timeDelta;
- _frameCount = 0;
- _startFrameTime = timeNow;
- }
- }
- }
- [System.Serializable]
- [StructLayout(LayoutKind.Sequential, Pack=1)]
- public class VideoEncoderHints
- {
- public VideoEncoderHints()
- {
- SetDefaults();
- }
- public void SetDefaults()
- {
- averageBitrate = 0;
- maximumBitrate = 0;
- quality = 1.0f;
- keyframeInterval = 0;
- allowFastStartStreamingPostProcess = true;
- supportTransparency = false;
- useHardwareEncoding = true;
- injectStereoPacking = NoneAutoCustom.Auto;
- stereoPacking = StereoPacking.None;
- injectSphericalVideoLayout = NoneAutoCustom.Auto;
- sphericalVideoLayout = SphericalVideoLayout.None;
- enableFragmentedWriting = false;
- movieFragmentInterval = 120; // Default to two minutes
- colourSpace = ColourSpace.Unknown;
- sourceWidth = 0;
- sourceHeight = 0;
- androidNoCaptureRotation = false;
- transparency = Transparency.None;
- androidVulkanPreTransform = AndroidVulkanPreTransform.None;
- }
- internal void Validate()
- {
- quality = Mathf.Clamp(quality, 0f, 1f);
- }
- [Tooltip("Average number of bits per second for the resulting video. Zero uses the codec defaults.")]
- public uint averageBitrate;
-
- [Tooltip("Maximum number of bits per second for the resulting video. Zero uses the codec defaults.")]
- public uint maximumBitrate;
-
- [Range(0f, 1f)] public float quality;
- [Tooltip("How often a keyframe is inserted. Zero uses the codec defaults.")]
- public uint keyframeInterval;
- // Only for MOV / MP4 files
- [Tooltip("Move the 'moov' atom in the video file from the end to the start of the file to make streaming start fast. Also known as 'Fast Start' in some encoders")]
- [MarshalAs(UnmanagedType.U1)]
- public bool allowFastStartStreamingPostProcess;
- // Currently only for HEVC and ProRes 4444 on macOS/iOS, and supported DirectShow codecs (eg Lagarith/Uncompressed) on Windows
- [Tooltip("Hints to the encoder to use the alpha channel for transparency if possible")]
- [MarshalAs(UnmanagedType.U1)]
- public bool supportTransparency;
- // Windows only (on macOS/iOS hardware is always used if available)
- [MarshalAs(UnmanagedType.U1)]
- public bool useHardwareEncoding;
- // Only for MP4 files on Windows
- [Tooltip("Inject atoms to define stereo video mode")]
- public NoneAutoCustom injectStereoPacking;
- [Tooltip("Inject atoms to define stereo video mode")]
- public StereoPacking stereoPacking;
- // Only for MP4 files on Windows
- [Tooltip("Inject atoms to define spherical video layout")]
- public NoneAutoCustom injectSphericalVideoLayout;
- [Tooltip("Inject atoms to define spherical video layout")]
- public SphericalVideoLayout sphericalVideoLayout;
- // Support fragmented writing of mov/mp4 on macOS/iOS
- [Tooltip("Enable fragmented writing support for QuickTime (mov, mp4) files")]
- [MarshalAs(UnmanagedType.U1)]
- public bool enableFragmentedWriting;
- // Not sure of sensible uppoer/lower bounds for this
- [Tooltip("The interval at which to write movie fragments in seconds")]
- [Range(0f, 300f)]
- public double movieFragmentInterval;
- public enum ColourSpace : int { Unknown = -1, Gamma = 0, Linear = 1 };
- public ColourSpace colourSpace;
- // The width and height of the source
- public int sourceWidth;
- public int sourceHeight;
- [MarshalAs(UnmanagedType.U1)]
- public bool androidNoCaptureRotation;
- // Transparency
- [Tooltip("Transparency mode")]
- public Transparency transparency;
- // Android Vulkan only - not user configurable
- public AndroidVulkanPreTransform androidVulkanPreTransform;
- }
- [System.Serializable]
- [StructLayout(LayoutKind.Sequential, Pack=1)]
- public class ImageEncoderHints
- {
- public ImageEncoderHints()
- {
- SetDefaults();
- }
- public void SetDefaults()
- {
- quality = 0.85f;
- supportTransparency = false;
- colourSpace = ColourSpace.Unknown;
- sourceWidth = 0;
- sourceHeight = 0;
- transparency = Transparency.None;
- androidVulkanPreTransform = AndroidVulkanPreTransform.None;
- }
- internal void Validate()
- {
- quality = Mathf.Clamp(quality, 0f, 1f);
- }
- // Currently only affects JPG and HEIF formats (macOS only)
- [Range(0f, 1f)] public float quality;
- // Currently only for PNG
- [Tooltip("Hints to the encoder to use the alpha channel for transparency if possible")]
- [MarshalAs(UnmanagedType.U1)]
- public bool supportTransparency;
- public enum ColourSpace : int { Unknown = -1, Gamma = 0, Linear = 1 };
- public ColourSpace colourSpace;
- // The width and height of the source
- public int sourceWidth;
- public int sourceHeight;
- // Transparency
- [Tooltip("Transparency mode")]
- public Transparency transparency;
- [MarshalAs(UnmanagedType.U1)]
- public AndroidVulkanPreTransform androidVulkanPreTransform;
- }
- [System.Serializable]
- public class EncoderHints
- {
- public EncoderHints()
- {
- SetDefaults();
- }
- public void SetDefaults()
- {
- videoHints = new VideoEncoderHints();
- imageHints = new ImageEncoderHints();
- }
- public VideoEncoderHints videoHints;
- public ImageEncoderHints imageHints;
- }
- /// <summary>
- /// Base class wrapping common capture functionality
- /// </summary>
- public partial class CaptureBase : MonoBehaviour
- {
- private const string DocEditionsURL = "https://www.renderheads.com/content/docs/AVProMovieCapture/articles/download.html#editions";
- public enum Resolution
- {
- POW2_8192x8192,
- POW2_8192x4096,
- POW2_4096x4096,
- POW2_4096x2048,
- POW2_2048x4096,
- UHD_3840x2160,
- UHD_3840x2048,
- UHD_3840x1920,
- UHD_2560x1440,
- POW2_2048x2048,
- POW2_2048x1024,
- HD_1920x1080,
- HD_1280x720,
- SD_1024x768,
- SD_800x600,
- SD_800x450,
- SD_640x480,
- SD_640x360,
- SD_320x240,
- Original,
- Custom,
- }
- public enum CubemapDepth
- {
- Depth_24 = 24,
- Depth_16 = 16,
- Depth_Zero = 0,
- }
- public enum CubemapResolution
- {
- POW2_8192 = 8192,
- POW2_4096 = 4096,
- POW2_2048 = 2048,
- POW2_1024 = 1024,
- POW2_512 = 512,
- POW2_256 = 256,
- }
- public enum AntiAliasingLevel
- {
- UseCurrent,
- ForceNone,
- ForceSample2,
- ForceSample4,
- ForceSample8,
- }
- public enum DownScale
- {
- Original = 1,
- Half = 2,
- Quarter = 4,
- Eighth = 8,
- Sixteenth = 16,
- Custom = 100,
- }
- public enum OutputPath
- {
- RelativeToProject,
- RelativeToPeristentData,
- Absolute,
- RelativeToDesktop,
- RelativeToPictures,
- RelativeToVideos,
- PhotoLibrary,
- RelativeToTemporaryCachePath
- }
- public enum FrameUpdateMode
- {
- Automatic,
- Manual,
- }
- /*public enum OutputExtension
- {
- AVI,
- MP4,
- PNG,
- Custom = 100,
- }*/
- #if false
- [System.Serializable]
- public class WindowsPostCaptureSettings
- {
- [SerializeField]
- [Tooltip("Move the 'moov' atom in the MP4 file from the end to the start of the file to make streaming start fast. Also called 'Fast Start' in some encoders")]
- public bool writeFastStartStreamingForMp4 = true;
- }
- [System.Serializable]
- public class PostCaptureSettings
- {
- [SerializeField]
- [Tooltip("Move the 'moov' atom in the MP4 file from the end to the start of the file to make streaming start fast. Also called 'Fast Start' in some encoders")]
- public WindowsPostCaptureSettings windows = new WindowsPostCaptureSettings();
- }
- [SerializeField] PostCaptureSettings _postCaptureSettings = new PostCaptureSettings();
- #endif
- [SerializeField] EncoderHints _encoderHintsWindows = new EncoderHints();
- [SerializeField] EncoderHints _encoderHintsMacOS = new EncoderHints();
- [SerializeField] EncoderHints _encoderHintsIOS = new EncoderHints();
- [SerializeField] EncoderHints _encoderHintsAndroid = new EncoderHints();
- // General options
- [SerializeField] KeyCode _captureKey = KeyCode.None;
- [SerializeField] bool _isRealTime = true;
- [SerializeField] bool _persistAcrossSceneLoads = false;
- // Start options
- [SerializeField] StartTriggerMode _startTrigger = StartTriggerMode.Manual;
- [SerializeField] StartDelayMode _startDelay = StartDelayMode.None;
- [SerializeField] float _startDelaySeconds = 0f;
- // Stop options
- [SerializeField] StopMode _stopMode = StopMode.None;
- // TODO: add option to pause instead of stop?
- [SerializeField] int _stopFrames = 0;
- [SerializeField] float _stopSeconds = 0f;
- #pragma warning disable 0414 // "is assigned but its value is never used"
- [SerializeField] bool _pauseCaptureOnAppPause = true;
- // Video options
- public static readonly string[] DefaultVideoCodecPriorityWindows = { "H264",
- "HEVC",
- "Lagarith Lossless Codec",
- "Uncompressed",
- "x264vfw - H.264/MPEG-4 AVC codec",
- "Xvid MPEG-4 Codec" };
- public static readonly string[] DefaultVideoCodecPriorityMacOS = { "H264",
- "HEVC",
- "Apple ProRes 422",
- "Apple ProRes 4444" };
- public static readonly string[] DefaultVideoCodecPriorityAndroid = { "H264",
- "HEVC"/*,
- "VP8",
- "VP9"*/ };
- public static readonly string[] DefaultAudioCodecPriorityWindows = { "AAC",
- "FLAC",
- "Uncompressed" };
- public static readonly string[] DefaultAudioCodecPriorityMacOS = { "AAC",
- "FLAC",
- "Apple Lossless",
- "Linear PCM",
- "Uncompresssed" };
- public static readonly string[] DefaultAudioCodecPriorityIOS = { "AAC",
- "FLAC",
- "Apple Lossless",
- "Linear PCM",
- "Uncompresssed" };
- public static readonly string[] DefaultAudioCodecPriorityAndroid = {"AAC"/*,
- "FLAC",
- "OPUS"*/};
- public static readonly string[] DefaultAudioCaptureDevicePriorityWindow = { "Microphone (Realtek Audio)", "Stereo Mix", "What U Hear", "What You Hear", "Waveout Mix", "Mixed Output" };
- public static readonly string[] DefaultAudioCaptureDevicePriorityMacOS = { };
- public static readonly string[] DefaultAudioCaptureDevicePriorityIOS = { };
- public static readonly string[] DefaultAudioCaptureDevicePriorityAndroid = { };
- [SerializeField] string[] _videoCodecPriorityWindows = DefaultVideoCodecPriorityWindows;
- [SerializeField] string[] _videoCodecPriorityMacOS = DefaultVideoCodecPriorityMacOS;
- #pragma warning disable 0414 // "is assigned but its value is never used"
- [SerializeField] string[] _videoCodecPriorityAndroid = DefaultVideoCodecPriorityAndroid;
- #pragma warning restore 0414
- [SerializeField] string[] _audioCodecPriorityWindows = DefaultAudioCodecPriorityWindows;
- [SerializeField] string[] _audioCodecPriorityMacOS = DefaultAudioCodecPriorityMacOS;
- #pragma warning disable 0414 // "is assigned but its value is never used"
- [SerializeField] string[] _audioCodecPriorityAndroid = DefaultAudioCodecPriorityAndroid;
- #pragma warning restore 0414
- [SerializeField] float _frameRate = 30f;
- [Tooltip("Timelapse scale makes the frame capture run at a fraction of the target frame rate. Default value is 1")]
- [SerializeField] int _timelapseScale = 1;
- [Tooltip("Manual update mode requires user to call FrameUpdate() each time a frame is ready")]
- [SerializeField] FrameUpdateMode _frameUpdateMode = FrameUpdateMode.Automatic;
- [SerializeField] DownScale _downScale = DownScale.Original;
- [SerializeField] Vector2 _maxVideoSize = Vector2.zero;
- #pragma warning disable 414
- [SerializeField, Range(-1, 128)] int _forceVideoCodecIndexWindows = -1;
- [SerializeField, Range(-1, 128)] int _forceVideoCodecIndexMacOS = 0;
- [SerializeField, Range(0, 128)] int _forceVideoCodecIndexIOS = 0;
- [SerializeField, Range(0, 128)] int _forceVideoCodecIndexAndroid = 0;
- [SerializeField, Range(-1, 128)] int _forceAudioCodecIndexWindows = -1;
- [SerializeField, Range(-1, 128)] int _forceAudioCodecIndexMacOS = 0;
- [SerializeField, Range(0, 128)] int _forceAudioCodecIndexIOS = 0;
- [SerializeField, Range(0, 128)] int _forceAudioCodecIndexAndroid = -1;
- #pragma warning restore 414
- [SerializeField] bool _flipVertically = false;
- [Tooltip("Flushing the GPU during each capture results in less latency, but can slow down rendering performance for complex scenes.")]
- [SerializeField] bool _forceGpuFlush = false;
- [Tooltip("This option can help issues where skinning is used, or other animation/rendering effects that only complete later in the frame.")]
- [SerializeField] protected bool _useWaitForEndOfFrame = true;
- [Tooltip("Update the media gallery")]
- [SerializeField] protected bool _androidUpdateMediaGallery = true;
- [Tooltip("Portrait captures may be rotated 90° to better utilise the encoder, check this to disable the rotation at the risk of not being able to capture the full vertical resolution.")]
- [SerializeField] bool _androidNoCaptureRotation = false;
- [Tooltip("Log the start and stop of the capture. Disable this for less garbage generation.")]
- [SerializeField] bool _logCaptureStartStop = true;
- // Audio options
- [SerializeField] AudioCaptureSource _audioCaptureSource = AudioCaptureSource.None;
- [SerializeField] UnityAudioCapture _unityAudioCapture = null;
- [SerializeField, Range(0, 32)] int _forceAudioInputDeviceIndex = 0;
- [SerializeField, Range(8000, 96000)] int _manualAudioSampleRate = 48000;
- [SerializeField, Range(1, 8)] int _manualAudioChannelCount = 2;
- // Output options
- [SerializeField] protected OutputTarget _outputTarget = OutputTarget.VideoFile;
- public OutputTarget OutputTarget
- {
- get { return _outputTarget; }
- set { _outputTarget = value; }
- }
- public const OutputPath DefaultOutputFolderType = OutputPath.RelativeToProject;
- private const string DefaultOutputFolderPath = "Captures";
- [SerializeField] OutputPath _outputFolderType = DefaultOutputFolderType;
- [SerializeField] string _outputFolderPath = DefaultOutputFolderPath;
- [SerializeField] string _filenamePrefix = "MovieCapture";
- [SerializeField] bool _appendFilenameTimestamp = true;
- [SerializeField] bool _allowManualFileExtension = false;
- [SerializeField] string _filenameExtension = "mp4";
- [SerializeField] string _namedPipePath = @"\\.\pipe\test_pipe";
- public OutputPath OutputFolder
- {
- get { return _outputFolderType; }
- set { _outputFolderType = value; }
- }
- public string OutputFolderPath
- {
- get { return _outputFolderPath; }
- set { _outputFolderPath = value; }
- }
- public string FilenamePrefix
- {
- get { return _filenamePrefix; }
- set { _filenamePrefix = value; }
- }
- public bool AppendFilenameTimestamp
- {
- get { return _appendFilenameTimestamp; }
- set { _appendFilenameTimestamp = value; }
- }
- public bool AllowManualFileExtension
- {
- get { return _allowManualFileExtension; }
- set { _allowManualFileExtension = value; }
- }
- public string FilenameExtension
- {
- get { return _filenameExtension; }
- set { _filenameExtension = value; }
- }
- public string NamedPipePath
- {
- get { return _namedPipePath; }
- set { _namedPipePath = value; }
- }
- [SerializeField] int _imageSequenceStartFrame = 0;
- [SerializeField, Range(2, 12)] int _imageSequenceZeroDigits = 6;
- #pragma warning disable 414
- [SerializeField] ImageSequenceFormat _imageSequenceFormatWindows = ImageSequenceFormat.PNG;
- [SerializeField] ImageSequenceFormat _imageSequenceFormatMacOS = ImageSequenceFormat.PNG;
- [SerializeField] ImageSequenceFormat _imageSequenceFormatIOS = ImageSequenceFormat.PNG;
- [SerializeField] ImageSequenceFormat _imageSequenceFormatAndroid = ImageSequenceFormat.PNG;
- #pragma warning restore 414
- public int ImageSequenceStartFrame
- {
- get { return _imageSequenceStartFrame; }
- set { _imageSequenceStartFrame = value; }
- }
- public int ImageSequenceZeroDigits
- {
- get { return _imageSequenceZeroDigits; }
- set { _imageSequenceZeroDigits = Mathf.Clamp(_imageSequenceZeroDigits, 2, 12); }
- }
- // Camera specific options
- [SerializeField] protected Resolution _renderResolution = Resolution.Original;
- [SerializeField] protected Vector2 _renderSize = Vector2.one;
- [SerializeField] protected int _renderAntiAliasing = -1;
- // Motion blur options
- [SerializeField] protected bool _useMotionBlur = false;
- [SerializeField, Range(0, 64)] protected int _motionBlurSamples = 16;
- [SerializeField] protected Camera[] _motionBlurCameras = null;
- [SerializeField] protected MotionBlur _motionBlur;
- public bool UseMotionBlur
- {
- get { return _useMotionBlur; }
- set { _useMotionBlur = value; }
- }
- public int MotionBlurSamples
- {
- get { return _motionBlurSamples; }
- set { _motionBlurSamples = (int)Mathf.Clamp((float)value, 0f, 64f); }
- }
- public Camera[] MotionBlurCameras
- {
- get { return _motionBlurCameras; }
- set { _motionBlurCameras = value; }
- }
- public MotionBlur MotionBlur
- {
- get { return _motionBlur; }
- set { _motionBlur = value; }
- }
- // Performance options
- [SerializeField] bool _allowVSyncDisable = true;
- [SerializeField] protected bool _supportTextureRecreate = false;
- // Other options
- [SerializeField] int _minimumDiskSpaceMB = -1;
- #if AVPRO_MOVIECAPTURE_PLAYABLES_SUPPORT
- [SerializeField] TimelineController _timelineController = null;
- #endif
- #if AVPRO_MOVIECAPTURE_VIDEOPLAYER_SUPPORT
- [SerializeField] VideoPlayerController _videoPlayerController = null;
- #endif
- //public bool _allowFrameRateChange = true;
- protected Texture2D _texture;
- protected int _handle = -1;
- protected int _sourceWidth, _sourceHeight;
- protected int _targetWidth, _targetHeight;
- protected bool _capturing = false;
- protected bool _paused = false;
- protected string _filePath;
- protected string _finalFilePath;
- protected FileInfo _fileInfo;
- protected NativePlugin.PixelFormat _pixelFormat = NativePlugin.PixelFormat.YCbCr422_YUY2;
- private Codec _selectedVideoCodec = null;
- private Codec _selectedAudioCodec = null;
- private Device _selectedAudioInputDevice = null;
- private int _oldVSyncCount = 0;
- //private int _oldTargetFrameRate = -1;
- private float _oldFixedDeltaTime = 0f;
- protected bool _isTopDown = true;
- protected bool _isDirectX11 = false;
- private bool _queuedStartCapture = false;
- private bool _queuedStopCapture = false;
- private float _captureStartTime = 0f;
- private float _capturePrePauseTotalTime = 0f;
- private float _timeSinceLastFrame = 0f;
- protected YieldInstruction _waitForEndOfFrame;
- private long _freeDiskSpaceMB;
- protected Transparency _Transparency = Transparency.None;
- public Transparency Transparency { get { return _Transparency; } }
- //
- protected RenderTexture _sideBySideTexture;
- protected Material _sideBySideMaterial;
- #if !(UNITY_EDITOR || UNITY_STANDALONE_WIN)
- private bool _wasCapturingOnPause = false;
- #endif
- private float _startDelayTimer;
- private bool _startPaused;
- private System.Action<FileWritingHandler> _beginFinalFileWritingAction;
- private System.Action<FileWritingHandler> _completedFileWritingAction;
- private List<FileWritingHandler> _pendingFileWrites = new List<FileWritingHandler>(4);
- private static HashSet<string> _activeFilePaths = new HashSet<string>();
- public static HashSet<string> ActiveFilePaths
- {
- get { return _activeFilePaths; }
- }
- public string LastFilePath
- {
- get { return _filePath; }
- }
- // Register for notification of when the final file writing begins
- public System.Action<FileWritingHandler> BeginFinalFileWritingAction
- {
- get { return _beginFinalFileWritingAction; }
- set { _beginFinalFileWritingAction = value; }
- }
-
- // Register for notification of when the final file writing completes
- public System.Action<FileWritingHandler> CompletedFileWritingAction
- {
- get { return _completedFileWritingAction; }
- set { _completedFileWritingAction = value; }
- }
- // Stats
- private CaptureStats _stats = new CaptureStats();
- private static bool _isInitialised = false;
- private static bool _isApplicationQuiting = false;
- public Resolution CameraRenderResolution
- {
- get { return _renderResolution; }
- set { _renderResolution = value; }
- }
- public Vector2 CameraRenderCustomResolution
- {
- get { return _renderSize; }
- set { _renderSize = value; }
- }
- public int CameraRenderAntiAliasing
- {
- get { return _renderAntiAliasing; }
- set { _renderAntiAliasing = value; }
- }
- public bool IsRealTime
- {
- get { return _isRealTime; }
- set { _isRealTime = value; }
- }
- public bool PersistAcrossSceneLoads
- {
- get { return _persistAcrossSceneLoads; }
- set { _persistAcrossSceneLoads = value; }
- }
- public AudioCaptureSource AudioCaptureSource
- {
- get { return _audioCaptureSource; }
- set { _audioCaptureSource = value; }
- }
- public int ManualAudioSampleRate
- {
- get { return _manualAudioSampleRate; }
- set { _manualAudioSampleRate = value; }
- }
- public int ManualAudioChannelCount
- {
- get { return _manualAudioChannelCount; }
- set { _manualAudioChannelCount = value; }
- }
- public UnityAudioCapture UnityAudioCapture
- {
- get { return _unityAudioCapture; }
- set { _unityAudioCapture = value; }
- }
- public int ForceAudioInputDeviceIndex
- {
- get { return _forceAudioInputDeviceIndex; }
- set { _forceAudioInputDeviceIndex = value; SelectAudioInputDevice(); }
- }
- public float FrameRate
- {
- get { return _frameRate; }
- set { _frameRate = Mathf.Clamp(value, 0.01f, 240f); }
- }
- public StartTriggerMode StartTrigger
- {
- get { return _startTrigger; }
- set { _startTrigger = value; }
- }
- public StartDelayMode StartDelay
- {
- get { return _startDelay; }
- set { _startDelay = value; }
- }
- public float StartDelaySeconds
- {
- get { return _startDelaySeconds; }
- set { _startDelaySeconds = Mathf.Max(0f, value); }
- }
- public StopMode StopMode
- {
- get { return _stopMode; }
- set { _stopMode = value; }
- }
- public int StopAfterFramesElapsed
- {
- get { return _stopFrames; }
- set { _stopFrames = Mathf.Max(0, value); }
- }
- public float StopAfterSecondsElapsed
- {
- get { return _stopSeconds; }
- set { _stopSeconds = Mathf.Max(0f, value); }
- }
- public CaptureStats CaptureStats
- {
- get { return _stats; }
- }
- public string[] VideoCodecPriorityWindows
- {
- get { return _videoCodecPriorityWindows; }
- set { _videoCodecPriorityWindows = value; SelectVideoCodec(false); }
- }
- public string[] VideoCodecPriorityMacOS
- {
- get { return _videoCodecPriorityMacOS; }
- set { _videoCodecPriorityMacOS = value; SelectVideoCodec(false); }
- }
- public string[] AudioCodecPriorityWindows
- {
- get { return _audioCodecPriorityWindows; }
- set { _audioCodecPriorityWindows = value; SelectAudioCodec(); }
- }
- public string[] AudioCodecPriorityMacOS
- {
- get { return _audioCodecPriorityMacOS; }
- set { _audioCodecPriorityMacOS = value; SelectAudioCodec(); }
- }
- public int TimelapseScale
- {
- get { return _timelapseScale; }
- set { _timelapseScale = value; }
- }
- public FrameUpdateMode FrameUpdate
- {
- get { return _frameUpdateMode; }
- set { _frameUpdateMode = value; }
- }
- public DownScale ResolutionDownScale
- {
- get { return _downScale; }
- set { _downScale = value; }
- }
- public Vector2 ResolutionDownscaleCustom
- {
- get { return _maxVideoSize; }
- set { _maxVideoSize = value; }
- }
- public bool FlipVertically
- {
- get { return _flipVertically; }
- set { _flipVertically = value; }
- }
- public bool UseWaitForEndOfFrame
- {
- get { return _useWaitForEndOfFrame; }
- set { _useWaitForEndOfFrame = value; }
- }
- public bool LogCaptureStartStop
- {
- get { return _logCaptureStartStop; }
- set { _logCaptureStartStop = value; }
- }
- #if false
- public PostCaptureSettings PostCapture
- {
- get { return _postCaptureSettings; }
- }
- #endif
- public bool AllowOfflineVSyncDisable
- {
- get { return _allowVSyncDisable; }
- set { _allowVSyncDisable = value; }
- }
- public bool SupportTextureRecreate
- {
- get { return _supportTextureRecreate; }
- set { _supportTextureRecreate = value; }
- }
- #if AVPRO_MOVIECAPTURE_PLAYABLES_SUPPORT
- public TimelineController TimelineController
- {
- get { return _timelineController; }
- set { _timelineController = value; }
- }
- #endif
- #if AVPRO_MOVIECAPTURE_VIDEOPLAYER_SUPPORT
- public VideoPlayerController VideoPlayerController
- {
- get { return _videoPlayerController; }
- set { _videoPlayerController = value; }
- }
- #endif
- public Codec SelectedVideoCodec
- {
- get { return _selectedVideoCodec; }
- }
- public Codec SelectedAudioCodec
- {
- get { return _selectedAudioCodec; }
- }
- public Device SelectedAudioInputDevice
- {
- get { return _selectedAudioInputDevice; }
- }
- public int NativeForceVideoCodecIndex
- {
- #if UNITY_EDITOR
- #if UNITY_EDITOR_WIN
- get { return _forceVideoCodecIndexWindows; }
- set { _forceVideoCodecIndexWindows = value; }
- #elif UNITY_EDITOR_OSX
- get { return _forceVideoCodecIndexMacOS; }
- set { _forceVideoCodecIndexMacOS = value; }
- #else
- get { return -1; }
- set { }
- #endif
- #else
- #if UNITY_STANDALONE_WIN
- get { return _forceVideoCodecIndexWindows; }
- set { _forceVideoCodecIndexWindows = value; }
- #elif UNITY_STANDALONE_OSX
- get { return _forceVideoCodecIndexMacOS; }
- set { _forceVideoCodecIndexMacOS = value; }
- #elif UNITY_IOS
- get { return _forceVideoCodecIndexIOS; }
- set { _forceVideoCodecIndexIOS = value; }
- #elif UNITY_ANDROID
- get { return _forceVideoCodecIndexAndroid; }
- set { _forceVideoCodecIndexAndroid = value; }
- #else
- get { return -1; }
- set { }
- #endif
- #endif
- }
- public int NativeForceAudioCodecIndex
- {
- #if UNITY_EDITOR
- #if UNITY_EDITOR_WIN
- get { return _forceAudioCodecIndexWindows; }
- set { _forceAudioCodecIndexWindows = value; }
- #elif UNITY_EDITOR_OSX
- get { return _forceAudioCodecIndexMacOS; }
- set { _forceAudioCodecIndexMacOS = value; }
- #else
- get { return -1; }
- set { }
- #endif
- #else
- #if UNITY_STANDALONE_WIN
- get { return _forceAudioCodecIndexWindows; }
- set { _forceAudioCodecIndexWindows = value; }
- #elif UNITY_STANDALONE_OSX
- get { return _forceAudioCodecIndexMacOS; }
- set { _forceAudioCodecIndexMacOS = value; }
- #elif UNITY_IOS
- get { return _forceAudioCodecIndexIOS; }
- set { _forceAudioCodecIndexIOS = value; }
- #elif UNITY_ANDROID
- get { return _forceAudioCodecIndexAndroid; }
- set { _forceAudioCodecIndexAndroid = value; }
- #else
- get { return -1; }
- set { }
- #endif
- #endif
- }
- public ImageSequenceFormat NativeImageSequenceFormat
- {
- #if UNITY_EDITOR
- #if UNITY_EDITOR_WIN
- get { return _imageSequenceFormatWindows; }
- set { _imageSequenceFormatWindows = value; }
- #elif UNITY_EDITOR_OSX
- get { return _imageSequenceFormatMacOS; }
- set { _imageSequenceFormatMacOS = value; }
- #else
- get { return ImageSequenceFormat.PNG; }
- set { }
- #endif
- #else
- #if UNITY_STANDALONE_WIN
- get { return _imageSequenceFormatWindows; }
- set { _imageSequenceFormatWindows = value; }
- #elif UNITY_STANDALONE_OSX
- get { return _imageSequenceFormatMacOS; }
- set { _imageSequenceFormatMacOS = value; }
- #elif UNITY_IOS
- get { return _imageSequenceFormatIOS; }
- set { _imageSequenceFormatIOS = value; }
- #elif UNITY_ANDROID
- get { return _imageSequenceFormatAndroid; }
- set { _imageSequenceFormatAndroid = value; }
- #else
- get { return ImageSequenceFormat.PNG; }
- set { }
- #endif
- #endif
- }
- protected static NativePlugin.Platform GetCurrentPlatform()
- {
- NativePlugin.Platform result = NativePlugin.Platform.Unknown;
- #if UNITY_EDITOR
- #if UNITY_EDITOR_WIN
- result = NativePlugin.Platform.Windows;
- #elif UNITY_EDITOR_OSX
- result = NativePlugin.Platform.macOS;
- #endif
- #else
- #if UNITY_STANDALONE_WIN
- result = NativePlugin.Platform.Windows;
- #elif UNITY_STANDALONE_OSX
- result = NativePlugin.Platform.macOS;
- #elif UNITY_IOS
- result = NativePlugin.Platform.iOS;
- #elif UNITY_ANDROID
- result = NativePlugin.Platform.Android;
- #endif
- #endif
- return result;
- }
- public EncoderHints GetEncoderHints(NativePlugin.Platform platform = NativePlugin.Platform.Current)
- {
- EncoderHints result = null;
- if (platform == NativePlugin.Platform.Current)
- {
- platform = GetCurrentPlatform();
- }
- switch (platform)
- {
- case NativePlugin.Platform.Windows:
- result = _encoderHintsWindows;
- break;
- case NativePlugin.Platform.macOS:
- result = _encoderHintsMacOS;
- break;
- case NativePlugin.Platform.iOS:
- result = _encoderHintsIOS;
- break;
- case NativePlugin.Platform.Android:
- result = _encoderHintsAndroid;
- break;
- }
- return result;
- }
- public void SetEncoderHints(EncoderHints hints, NativePlugin.Platform platform = NativePlugin.Platform.Current)
- {
- if (platform == NativePlugin.Platform.Current)
- {
- platform = GetCurrentPlatform();
- }
- switch (platform)
- {
- case NativePlugin.Platform.Windows:
- _encoderHintsWindows = hints;
- break;
- case NativePlugin.Platform.macOS:
- _encoderHintsMacOS = hints;
- break;
- case NativePlugin.Platform.iOS:
- _encoderHintsIOS = hints;
- break;
- case NativePlugin.Platform.Android:
- _encoderHintsAndroid = hints;
- break;
- }
- }
- #if UNITY_ANDROID && !UNITY_EDITOR
- protected static AndroidJavaObject s_ActivityContext = null;
- protected static AndroidJavaClass s_Interface = null;
- #endif
- public static void UpdateMediaGallery( string videoFilePath )
- {
- if( videoFilePath != null )
- {
- #if UNITY_ANDROID && !UNITY_EDITOR
- // Update video gallery on Android
- if( s_Interface != null )
- {
- s_Interface.CallStatic("UpdateMediaGallery", videoFilePath);
- }
- #endif
- }
- }
- protected virtual void Awake()
- {
- if (!_isInitialised)
- {
- #if UNITY_ANDROID && !UNITY_EDITOR
- // Get the activity context
- if (s_ActivityContext == null)
- {
- AndroidJavaClass activityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
- if (activityClass != null)
- {
- s_ActivityContext = activityClass.GetStatic<AndroidJavaObject>("currentActivity");
- }
- }
- s_Interface = new AndroidJavaClass("com.renderheads.AVPro.MovieCapture.Manager");
- s_Interface.CallStatic("setContext", s_ActivityContext);
- #endif
- try
- {
- string pluginVersionString = NativePlugin.GetPluginVersionString();
- // Check that the plugin version number is not too old
- if (!pluginVersionString.StartsWith(NativePlugin.ExpectedPluginVersion))
- {
- Debug.LogWarning("[AVProMovieCapture] Plugin version number " + pluginVersionString + " doesn't match the expected version number " + NativePlugin.ExpectedPluginVersion + ". It looks like the plugin didn't upgrade correctly. To resolve this please restart Unity and try to upgrade the package again.");
- }
- #if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
- #if !UNITY_2017_1_OR_NEWER
- if (SystemInfo.graphicsDeviceVersion.StartsWith("Metal"))
- {
- Debug.LogError("[AVProMovieCapture] Metal is not supported below Unity 2017, please switch to OpenGLCore in Player Settings.");
- return;
- }
- #endif
- #elif !UNITY_EDITOR && UNITY_IOS && !UNITY_2017_1_OR_NEWER
- if (Application.isPlaying)
- {
- Debug.LogError("[AVProMovieCapture] iOS is not supported below Unity 2017.");
- return;
- }
- #endif
- if (NativePlugin.Init())
- {
- Debug.Log("[AVProMovieCapture] Init version: " + NativePlugin.ScriptVersion + " (plugin v" + pluginVersionString +") with GPU " + SystemInfo.graphicsDeviceName + " " + SystemInfo.graphicsDeviceVersion + " OS: " + SystemInfo.operatingSystem);
- _isInitialised = true;
- }
- else
- {
- Debug.LogError("[AVProMovieCapture] Failed to initialise plugin on version: " + NativePlugin.ScriptVersion + " (plugin v" + pluginVersionString + ") with GPU " + SystemInfo.graphicsDeviceName + " " + SystemInfo.graphicsDeviceVersion + " OS: " + SystemInfo.operatingSystem);
- }
- }
- catch (DllNotFoundException e)
- {
- string missingDllMessage = string.Empty;
- missingDllMessage = "Unity couldn't find the plugin DLL. Please select the native plugin files in 'Plugins/RenderHeads/AVProMovieCapture/Runtime/Plugins' folder and select the correct platform in the Inspector.";
- Debug.LogError("[AVProMovieCapture] " + missingDllMessage);
- #if UNITY_EDITOR
- UnityEditor.EditorUtility.DisplayDialog("Plugin files not found", missingDllMessage, "Ok");
- #endif
- throw e;
- }
- }
- _isDirectX11 = SystemInfo.graphicsDeviceVersion.StartsWith("Direct3D 11");
- // Moved to Start() now due to no audio on Andorid when creating a capture component in script
- // SelectVideoCodec();
- // SelectAudioCodec();
- // SelectAudioInputDevice();
- if (_persistAcrossSceneLoads)
- {
- GameObject.DontDestroyOnLoad(this.gameObject);
- }
- }
- static CaptureBase()
- {
- #if UNITY_EDITOR
- SetupEditorPlayPauseSupport();
- #endif
- }
- public virtual void Start()
- {
- // Moved from Awake() now due to no audio on Andorid when creating a capture component in script
- SelectVideoCodec();
- SelectAudioCodec();
- SelectAudioInputDevice();
- Application.runInBackground = true;
- _waitForEndOfFrame = new WaitForEndOfFrame();
- if (_startTrigger == StartTriggerMode.OnStart)
- {
- StartCapture();
- }
- }
- // Select the best codec based on criteria
- private static bool SelectCodec(ref Codec codec, CodecList codecList, int forceCodecIndex, string[] codecPriorityList, MediaApi matchMediaApi, bool allowFallbackToFirstCodec, bool logFallbackWarning)
- {
- codec = null;
- // The user has specified their own codec index
- if (forceCodecIndex >= 0)
- {
- if (forceCodecIndex < codecList.Count)
- {
- codec = codecList.Codecs[forceCodecIndex];
- }
- }
- else
- {
- // The user has specified an ordered list of codec name to search for
- if (codecPriorityList != null && codecPriorityList.Length > 0)
- {
- foreach (string codecName in codecPriorityList)
- {
- codec = codecList.FindCodec(codecName.Trim(), matchMediaApi);
- if (codec != null)
- {
- break;
- }
- }
- }
- }
- // If the found codec doesn't match the required MediaApi, set it to null
- if (codec != null && matchMediaApi != MediaApi.Unknown)
- {
- if (codec.MediaApi != matchMediaApi)
- {
- codec = null;
- }
- }
- // Fallback to the first codec
- if (codec == null && allowFallbackToFirstCodec)
- {
- if (codecList.Count > 0)
- {
- if (matchMediaApi != MediaApi.Unknown)
- {
- codec = codecList.GetFirstWithMediaApi(matchMediaApi);
- }
- else
- {
- codec = codecList.Codecs[0];
- }
- if (logFallbackWarning)
- {
- Debug.LogWarning("[AVProMovieCapture] Codec not found. Using the first codec available.");
- }
- }
- }
- return (codec != null);
- }
- public Codec SelectVideoCodec(bool isStartingCapture = false)
- {
- _selectedVideoCodec = null;
- #if UNITY_EDITOR_WIN || (!UNITY_EDITOR && UNITY_STANDALONE_WIN)
- SelectCodec(ref _selectedVideoCodec, CodecManager.VideoCodecs, NativeForceVideoCodecIndex, _videoCodecPriorityWindows, MediaApi.Unknown, true, isStartingCapture);
- #elif UNITY_EDITOR_OSX || (!UNITY_EDITOR && UNITY_STANDALONE_OSX)
- SelectCodec(ref _selectedVideoCodec, CodecManager.VideoCodecs, NativeForceVideoCodecIndex, _videoCodecPriorityMacOS, MediaApi.Unknown, true, isStartingCapture);
- #elif !UNITY_EDITOR && UNITY_IOS
- SelectCodec(ref _selectedVideoCodec, CodecManager.VideoCodecs, NativeForceVideoCodecIndex, null, MediaApi.Unknown, true, isStartingCapture);
- #elif !UNITY_EDITOR && UNITY_ANDROID
- SelectCodec(ref _selectedVideoCodec, CodecManager.VideoCodecs, NativeForceVideoCodecIndex, _videoCodecPriorityAndroid, MediaApi.Unknown, true, isStartingCapture);
- #endif
- if (isStartingCapture && _selectedVideoCodec == null)
- {
- Debug.LogError("[AVProMovieCapture] Failed to select a suitable video codec");
- }
- return _selectedVideoCodec;
- }
- public Codec SelectAudioCodec()
- {
- _selectedAudioCodec = null;
- if (_audioCaptureSource != AudioCaptureSource.None)
- {
- #if UNITY_EDITOR_WIN || (!UNITY_EDITOR && UNITY_STANDALONE_WIN)
- // Audio codec selection requires a video codec to be selected first on Windows
- if (_selectedVideoCodec != null)
- {
- SelectCodec(ref _selectedAudioCodec, CodecManager.AudioCodecs, NativeForceAudioCodecIndex, _audioCodecPriorityWindows, _selectedVideoCodec.MediaApi, true, false);
- }
- #elif UNITY_EDITOR_OSX || (!UNITY_EDITOR && UNITY_STANDALONE_OSX)
- SelectCodec(ref _selectedAudioCodec, CodecManager.AudioCodecs, NativeForceAudioCodecIndex, _audioCodecPriorityMacOS, MediaApi.Unknown, true, false);
- #elif !UNITY_EDITOR && UNITY_IOS
- SelectCodec(ref _selectedAudioCodec, CodecManager.AudioCodecs, NativeForceAudioCodecIndex, null, MediaApi.Unknown, true, false);
- #elif !UNITY_EDITOR && UNITY_ANDROID
- SelectCodec(ref _selectedAudioCodec, CodecManager.AudioCodecs, NativeForceAudioCodecIndex, null, MediaApi.Unknown, true, false);
- #endif
- if (_selectedAudioCodec == null)
- {
- //Debug.LogError("[AVProMovieCapture] Failed to select a suitable audio codec");
- }
- }
- return _selectedAudioCodec;
- }
- public Device SelectAudioInputDevice()
- {
- _selectedAudioInputDevice = null;
- if (_audioCaptureSource == AudioCaptureSource.Microphone)
- {
- // Audio input device selection requires a video codec to be selected first
- if (_selectedVideoCodec != null)
- {
- if (_forceAudioInputDeviceIndex >= 0 && _forceAudioInputDeviceIndex < DeviceManager.AudioInputDevices.Count)
- {
- _selectedAudioInputDevice = DeviceManager.AudioInputDevices.Devices[_forceAudioInputDeviceIndex];
- }
- // If the found codec doesn't match the required MediaApi, set it to null
- if (_selectedAudioInputDevice != null && _selectedAudioInputDevice.MediaApi != _selectedVideoCodec.MediaApi)
- {
- _selectedAudioInputDevice = null;
- }
- // Fallback to the first device
- if (_selectedAudioInputDevice == null)
- {
- if (DeviceManager.AudioInputDevices.Count > 0)
- {
- _selectedAudioInputDevice = DeviceManager.AudioInputDevices.GetFirstWithMediaApi(_selectedVideoCodec.MediaApi);
- }
- }
- }
- }
- return _selectedAudioInputDevice;
- }
- public static Vector2 GetRecordingResolution(int width, int height, DownScale downscale, Vector2 maxVideoSize)
- {
- int targetWidth = width;
- int targetHeight = height;
- if (downscale != DownScale.Custom)
- {
- targetWidth /= (int)downscale;
- targetHeight /= (int)downscale;
- }
- else
- {
- if (maxVideoSize.x >= 1.0f && maxVideoSize.y >= 1.0f)
- {
- targetWidth = Mathf.FloorToInt(maxVideoSize.x);
- targetHeight = Mathf.FloorToInt(maxVideoSize.y);
- }
- }
- // Some codecs like Lagarith in YUY2 mode need size to be multiple of 4
- targetWidth = NextMultipleOf4(targetWidth);
- targetHeight = NextMultipleOf4(targetHeight);
- return new Vector2(targetWidth, targetHeight);
- }
- public void SelectRecordingResolution(int width, int height)
- {
- _sourceWidth = width;
- _sourceHeight = height;
- _targetWidth = width;
- _targetHeight = height;
- if (_downScale != DownScale.Custom)
- {
- _targetWidth /= (int)_downScale;
- _targetHeight /= (int)_downScale;
- }
- else
- {
- if (_maxVideoSize.x >= 1.0f && _maxVideoSize.y >= 1.0f)
- {
- _targetWidth = Mathf.FloorToInt(_maxVideoSize.x);
- _targetHeight = Mathf.FloorToInt(_maxVideoSize.y);
- }
- }
- // Some codecs like Lagarith in YUY2 mode need size to be multiple of 4
- _targetWidth = NextMultipleOf4(_targetWidth);
- _targetHeight = NextMultipleOf4(_targetHeight);
- }
- public virtual void OnDestroy()
- {
- _waitForEndOfFrame = null;
- StopCapture(true, true);
- FreePendingFileWrites();
- // Make sure there are no other capture instances running and then deinitialise the plugin
- if (_isApplicationQuiting && _isInitialised)
- {
- // TODO: would it be faster to just look for _pendingFileWrites?
- bool anyCapturesRunning = false;
- #if UNITY_EDITOR
- // In editor we have to search hidden objects as well, as the editor window components are created hidden
- CaptureBase[] captures = (CaptureBase[])Resources.FindObjectsOfTypeAll(typeof(CaptureBase));
- #else
- CaptureBase[] captures = (CaptureBase[])Component.FindObjectsOfType(typeof(CaptureBase));
- #endif
- foreach (CaptureBase capture in captures)
- {
- if (capture != null && capture.IsCapturing())
- {
- anyCapturesRunning = true;
- break;
- }
- }
- if (!anyCapturesRunning)
- {
- NativePlugin.Deinit();
- _isInitialised = false;
- }
- }
- }
- private void FreePendingFileWrites()
- {
- foreach (FileWritingHandler handler in _pendingFileWrites)
- {
- handler.Dispose();
- }
- _pendingFileWrites.Clear();
- }
- private void OnApplicationQuit()
- {
- _isApplicationQuiting = true;
- }
- /*
- private void OnApplicationPause(bool paused)
- {
- #if UNITY_IOS && !UNITY_EDITOR
- if (paused && IsCapturing())
- {
- Debug.LogWarning("Application is being paused, stopping capture...");
- StopCapture();
- }
- #endif
- }
- */
- #if !UNITY_EDITOR
- void OnApplicationFocus(bool focusStatus)
- {
- #if !(UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN)
- // Debug.Log("OnApplicationFocus: focusStatus: " + focusStatus);
- if( focusStatus )
- {
- if( _wasCapturingOnPause )
- {
- _wasCapturingOnPause = false;
- ResumeCapture();
- Debug.Log("OnApplicationFocus: capturing video again");
- }
- }
- #endif
- }
- void OnApplicationPause(bool pauseStatus)
- {
- #if !(UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN)
- // Debug.Log("OnApplicationPause: pauseStatus: " + pauseStatus);
- if( pauseStatus )
- {
- if( _pauseCaptureOnAppPause )
- {
- if( IsCapturing() )
- {
- _wasCapturingOnPause = true;
- #if !UNITY_IPHONE
- PauseCapture();
- #endif
- Debug.Log("OnApplicationPause: pausing video capture");
- }
- }
- }
- else
- {
- if( _pauseCaptureOnAppPause )
- {
- // Catch coming back from power off state when no lock screen
- OnApplicationFocus( true );
- }
- }
- #endif
- }
- #endif
- protected void EncodeTexture(Texture2D texture)
- {
- Color32[] bytes = texture.GetPixels32();
- GCHandle _frameHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
- EncodePointer(_frameHandle.AddrOfPinnedObject());
- if (_frameHandle.IsAllocated)
- {
- _frameHandle.Free();
- }
- }
- protected bool IsUsingUnityAudioComponent()
- {
- if (_outputTarget == OutputTarget.VideoFile && _unityAudioCapture != null)
- {
- if (_audioCaptureSource == AudioCaptureSource.Unity
- #if !AVPRO_MOVIECAPTURE_OFFLINE_AUDIOCAPTURE
- && _isRealTime
- #endif
- )
- {
- return true;
- }
- #if AVPRO_MOVIECAPTURE_WWISE_SUPPORT
- else if (_audioCaptureSource == AudioCaptureSource.Wwise && !_isRealTime)
- {
- return true;
- }
- #endif
- }
- return false;
- }
- protected bool IsUsingMotionBlur()
- {
- return (_useMotionBlur && !_isRealTime && _motionBlur != null);
- }
- public virtual void EncodePointer(System.IntPtr ptr)
- {
- if (!IsUsingUnityAudioComponent())
- {
- NativePlugin.EncodeFrame(_handle, ptr);
- }
- else
- {
- int audioDataLength = 0;
- System.IntPtr audioDataPtr = _unityAudioCapture.ReadData(out audioDataLength);
- if (audioDataLength > 0)
- {
- NativePlugin.EncodeFrameWithAudio(_handle, ptr, audioDataPtr, (uint)audioDataLength);
- }
- else
- {
- NativePlugin.EncodeFrame(_handle, ptr);
- }
- }
- }
- public bool IsPrepared()
- {
- return (_handle >= 0);
- }
- public bool IsCapturing()
- {
- return _capturing;
- }
- public bool IsPaused()
- {
- return _paused;
- }
- public int GetRecordingWidth()
- {
- return _targetWidth;
- }
- public int GetRecordingHeight()
- {
- return _targetHeight;
- }
- protected virtual string GenerateTimestampedFilename(string filenamePrefix, string filenameExtension)
- {
- // TimeSpan span = (DateTime.Now - DateTime.Now.Date);
- // string filename = string.Format("{0}-{1}-{2}-{3}-{4}s-{5}x{6}", filenamePrefix, DateTime.Now.Year, DateTime.Now.Month.ToString("D2"), DateTime.Now.Day.ToString("D2"), ((int)(span.TotalSeconds)).ToString(), _targetWidth, _targetHeight);
- // [MOZ] Use actual time in place of seconds
- string dateTime = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
- string filename = filenamePrefix;// string.Format("{0}_{1}_{2}x{3}", filenamePrefix, dateTime, _targetWidth, _targetHeight);
- // [MOZ] File extension is now optional
- if (!string.IsNullOrEmpty(filenameExtension))
- {
- filename = filename + "." + filenameExtension;
- }
- return filename;
- }
- #if UNITY_ANDROID && !UNITY_EDITOR
- private static string GetAndroidExternalDCIMStoragePath()
- {
- if( Application.platform != RuntimePlatform.Android )
- {
- return Application.persistentDataPath;
- }
- var jClass = new AndroidJavaClass("android.os.Environment");
- var dcimPath = jClass.CallStatic<AndroidJavaObject>("getExternalStoragePublicDirectory", jClass.GetStatic<string>("DIRECTORY_DCIM")).Call<string>("getAbsolutePath");
- return dcimPath;
- }
- #endif
- private static string GetFolder(OutputPath outputPathType, string path)
- {
- string folder = string.Empty;
- #if UNITY_IOS && !UNITY_EDITOR
- // iOS only supports a very limited subset of OutputPath so fix up and warn the user
- switch (outputPathType)
- {
- case OutputPath.RelativeToPeristentData:
- case OutputPath.PhotoLibrary:
- // These are fine
- break;
- case OutputPath.RelativeToProject:
- case OutputPath.Absolute:
- case OutputPath.RelativeToDesktop:
- case OutputPath.RelativeToVideos:
- case OutputPath.RelativeToPictures:
- case OutputPath.RelativeToTemporaryCachePath:
- // These are unsupported
- default:
- Debug.LogWarning(string.Format("[AVProMovieCapture] 'OutputPath.{0}' is not supported on iOS, defaulting to 'OutputPath.RelativeToPeristentData'", outputPathType));
- outputPathType = OutputPath.RelativeToPeristentData;
- break;
- }
- #endif
- #if UNITY_ANDROID && !UNITY_EDITOR
- // Android only supports a very limited subset of OutputPath so fix up and warn the user
- switch (outputPathType)
- {
- case OutputPath.RelativeToPeristentData:
- case OutputPath.RelativeToVideos:
- case OutputPath.Absolute:
- case OutputPath.RelativeToTemporaryCachePath:
- // These are fine
- break;
- case OutputPath.RelativeToProject:
- case OutputPath.RelativeToDesktop:
- case OutputPath.RelativeToPictures:
- case OutputPath.PhotoLibrary:
- // These are unsupported
- default:
- Debug.LogWarning(string.Format("[AVProMovieCapture] 'OutputPath.{0}' is not supported on Android, defaulting to 'OutputPath.RelativeToPeristentData'", outputPathType));
- outputPathType = OutputPath.RelativeToPeristentData;
- break;
- }
- #endif
- #if UNITY_EDITOR
- // Photo Library is unavailable in the editor
- if (outputPathType == OutputPath.PhotoLibrary)
- {
- Debug.LogWarning("[AVProMovieCapture] 'OutputPath.PhotoLibrary' is not available in the Unity Editor, defaulting to 'OutputPath.RelativeToProject'");
- outputPathType = OutputPath.RelativeToProject;
- }
- #endif
- switch (outputPathType)
- {
- case OutputPath.RelativeToProject:
- #if UNITY_STANDALONE_OSX
- // For standalone macOS builds this puts the path at the same level as the application bundle
- folder = System.IO.Path.GetFullPath(System.IO.Path.Combine(Application.dataPath, "../.."));
- #else
- folder = System.IO.Path.GetFullPath(System.IO.Path.Combine(Application.dataPath, ".."));
- #endif
- break;
- case OutputPath.RelativeToPeristentData:
- folder = System.IO.Path.GetFullPath(Application.persistentDataPath);
- break;
- case OutputPath.Absolute:
- break;
- case OutputPath.RelativeToDesktop:
- folder = System.Environment.GetFolderPath(System.Environment.SpecialFolder.DesktopDirectory);
- break;
- case OutputPath.RelativeToPictures:
- folder = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyPictures);
- break;
- case OutputPath.RelativeToVideos:
- #if UNITY_ANDROID && !UNITY_EDITOR
- folder = GetAndroidExternalDCIMStoragePath();
- #else
- #if NET_4_6
- folder = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyVideos);
- #else
- folder = System.Environment.GetFolderPath((System.Environment.SpecialFolder)14); // Older Mono doesn't have MyVideos defined - but still works!
- #endif
- #endif
- break;
- case OutputPath.PhotoLibrary:
- // use avpmc-photolibrary as the scheme
- folder = "avpmc-photolibrary:///"; // Three slashes are good as we don't need the host component
- break;
- case OutputPath.RelativeToTemporaryCachePath:
- folder = System.IO.Path.GetFullPath(Application.temporaryCachePath);
- break;
- }
- return System.IO.Path.Combine(folder, path);
- }
- private static string GenerateFilePath(OutputPath outputPathType, string path, string filename)
- {
- // Resolve folder
- string fileFolder = GetFolder(outputPathType, path);
- // Combine path and filename
- return System.IO.Path.Combine(fileFolder, filename);
- }
- protected static bool HasExtension(string path, string extension)
- {
- return path.ToLower().EndsWith(extension, StringComparison.OrdinalIgnoreCase);
- }
- protected void GenerateFilename()
- {
- string filename = string.Empty;
- if (_outputTarget == OutputTarget.VideoFile)
- {
- if (!_allowManualFileExtension)
- {
- if (_selectedVideoCodec == null)
- {
- SelectVideoCodec();
- SelectAudioCodec();
- }
- int videoCodec = (_selectedVideoCodec != null) ? _selectedVideoCodec.Index : -1;
- int audioCodec = (_selectedAudioCodec != null) ? _selectedAudioCodec.Index : -1;
- string[] extensions = NativePlugin.GetContainerFileExtensions(videoCodec, audioCodec);
- if (extensions != null && extensions.Length > 0)
- {
- _filenameExtension = extensions[0];
- }
- }
- if (_appendFilenameTimestamp)
- {
- filename = GenerateTimestampedFilename(_filenamePrefix, _filenameExtension);
- }
- else
- {
- filename = _filenamePrefix + "." + _filenameExtension;
- }
- }
- else if (_outputTarget == OutputTarget.ImageSequence)
- {
- // [MOZ] Made the enclosing folder uniquely named, easier for extraction on iOS and simplifies scripts for processing the frames
- string fileExtension = Utils.GetImageFileExtension(NativeImageSequenceFormat);
- filename = GenerateTimestampedFilename(_filenamePrefix, null) + "/frame" + string.Format("-%0{0}d.{1}", _imageSequenceZeroDigits, fileExtension);
- }
- else if (_outputTarget == OutputTarget.NamedPipe)
- {
- _filePath = _namedPipePath;
- }
- if (_outputTarget == OutputTarget.VideoFile ||
- _outputTarget == OutputTarget.ImageSequence)
- {
- OutputPath outputFolderType = _outputFolderType;
- string outputFolderPath = _outputFolderPath;
- _finalFilePath = null;
- #if UNITY_ANDROID && !UNITY_EDITOR
- if( _outputFolderType != OutputPath.RelativeToPeristentData && _outputTarget == OutputTarget.VideoFile )
- {
- // Where do we want to write the final file to?
- _finalFilePath = GenerateFilePath(_outputFolderType, _outputFolderPath, filename);
- // Capture to path relative to the project
- outputFolderType = OutputPath.RelativeToPeristentData;
- outputFolderPath = "Captures";
- // Create target final directory if it doesn't exist
- String finalDirectory = Path.GetDirectoryName(_finalFilePath);
- Debug.Log("[AVProMovieCapture]: finalDirectory = " + finalDirectory);
- if (!string.IsNullOrEmpty(finalDirectory) && !Directory.Exists(finalDirectory))
- {
- Directory.CreateDirectory(finalDirectory);
- }
- }
- #endif
- _filePath = GenerateFilePath(outputFolderType, outputFolderPath, filename);
- // Check to see if this filename is already in use
- if (ActiveFilePaths.Contains(_filePath))
- {
- // It is, strip the extension
- string extension = Path.GetExtension(_filePath);
- string name = Path.GetFileNameWithoutExtension(_filePath);
- string path = Path.GetDirectoryName(_filePath);
- string newPath = null;
- int i = 2;
- do
- {
- const string fmt = "{0} {1}";
- string newName = String.Format(fmt, name, i++);
- newPath = Path.Combine(path, newName);
- newPath = Path.ChangeExtension(newPath, extension);
- }
- while (ActiveFilePaths.Contains(newPath));
- _filePath = newPath;
- }
- ActiveFilePaths.Add(_filePath);
- #if !UNITY_EDITOR && (UNITY_STANDALONE_OSX || UNITY_IOS)
- if (_outputFolderType != OutputPath.PhotoLibrary)
- #endif
- {
- // Create target directory if it doesn't exist
- String directory = Path.GetDirectoryName(_filePath);
- if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
- {
- Directory.CreateDirectory(directory);
- }
- }
- }
- }
- public UnityAudioCapture FindOrCreateUnityAudioCapture(bool logWarnings)
- {
- UnityAudioCapture result = null;
- if (_audioCaptureSource == AudioCaptureSource.Unity)
- {
- Type audioCaptureType = null;
- if (_isRealTime)
- {
- audioCaptureType = typeof(CaptureAudioFromAudioListener);
- }
- else
- {
- #if AVPRO_MOVIECAPTURE_OFFLINE_AUDIOCAPTURE
- audioCaptureType = typeof(CaptureAudioFromAudioRenderer);
- #endif
- }
- if (audioCaptureType != null)
- {
- // Try to find an existing matching component locally
- result = (UnityAudioCapture)this.GetComponent(audioCaptureType);
- if (result == null)
- {
- // Try to find an existing matching component globally
- result = (UnityAudioCapture)GameObject.FindObjectOfType(audioCaptureType);
- }
- // No existing component was found, so create one
- if (result == null)
- {
- // Find a suitable gameobject to add the component to
- GameObject parentGameObject = null;
- if (_isRealTime)
- {
- // Find an AudioListener to attach the UnityAudioCapture component to
- AudioListener audioListener = this.GetComponent<AudioListener>();
- if (audioListener == null)
- {
- audioListener = GameObject.FindObjectOfType<AudioListener>();
- }
- parentGameObject = audioListener.gameObject;
- }
- else
- {
- parentGameObject = this.gameObject;
- }
- // Create the component
- if (_isRealTime)
- {
- if (parentGameObject != null)
- {
- result = (UnityAudioCapture)parentGameObject.AddComponent(audioCaptureType);
- if (logWarnings)
- {
- Debug.LogWarning("[AVProMovieCapture] Capturing audio from Unity without an UnityAudioCapture assigned so we had to create one manually (very slow). Consider adding a UnityAudioCapture component to your scene and assigned it to this MovieCapture component.");
- }
- }
- else
- {
- if (logWarnings)
- {
- Debug.LogWarning("[AVProMovieCapture] No AudioListener found in scene. Unable to capture audio from Unity.");
- }
- }
- }
- else
- {
- #if AVPRO_MOVIECAPTURE_OFFLINE_AUDIOCAPTURE
- result = (UnityAudioCapture)parentGameObject.AddComponent(audioCaptureType);
- ((CaptureAudioFromAudioRenderer)result).Capture = this;
- if (logWarnings)
- {
- Debug.LogWarning("[AVProMovieCapture] Capturing audio from Unity without an UnityAudioCapture assigned so we had to create one manually (very slow). Consider adding a UnityAudioCapture component to your scene and assigned it to this MovieCapture component.");
- }
- #endif
- }
- }
- else
- {
- if (logWarnings)
- {
- Debug.LogWarning("[AVProMovieCapture] Capturing audio from Unity without an UnityAudioCapture assigned so we had to search for one manually (very slow)");
- }
- }
- }
- }
- #if AVPRO_MOVIECAPTURE_WWISE_SUPPORT
- else if (_audioCaptureSource == AudioCaptureSource.Wwise)
- {
- Type audioCaptureType = null;
- if (!_isRealTime)
- {
- audioCaptureType = typeof(CaptureAudioFromWwise);
- }
- if (audioCaptureType != null)
- {
- // Try to find an existing matching component locally
- result = (UnityAudioCapture)this.GetComponent(audioCaptureType);
- if (result == null)
- {
- // Try to find an existing matching component globally
- result = (UnityAudioCapture)GameObject.FindObjectOfType(audioCaptureType);
- }
- // No existing component was found, so create one
- if (result == null)
- {
- result = (UnityAudioCapture)this.gameObject.AddComponent(audioCaptureType);
- }
- if (result)
- {
- ((CaptureAudioFromWwise)result).Capture = this;
- }
- }
- }
- #endif // AVPRO_MOVIECAPTURE_WWISE_SUPPORT
- return result;
- }
- private bool ValidateEditionFeatures()
- {
- bool canContinueCapture = true;
- if (NativePlugin.IsBasicEdition())
- {
- string issues = string.Empty;
- // Abortable issues
- if (SystemInfo.graphicsDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Direct3D12)
- {
- issues += "• D3D12 is not supported, please switch to D3D11. Aborting capture.\n";
- canContinueCapture = false;
- }
- if (this is CaptureFromCamera || this is CaptureFromTexture || this is CaptureFromCamera360 || this is CaptureFromCamera360ODS)
- {
- issues += "• Only CaptureFromScreen component is supported. Aborting capture.\n";
- canContinueCapture = false;
- }
- // Continuable issues
- if (canContinueCapture)
- {
- if (SelectedVideoCodec != null && SelectedVideoCodec.Name.IndexOf("H264") < 0)
- {
- issues += "• Only H264 video codec supported. Switching to H264\n";
- NativeForceVideoCodecIndex = 0;
- SelectVideoCodec(false);
- }
- if (SelectedAudioCodec != null && SelectedAudioCodec.Name.IndexOf("AAC") < 0)
- {
- issues += "• Only AAC audio codec supported. Switching to AAC\n";
- NativeForceAudioCodecIndex = 0;
- SelectAudioCodec();
- }
- if (!IsRealTime)
- {
- issues += "• Non-realtime captures are not supported. Switching to realtime capture mode.\n";
- IsRealTime = true;
- }
- if (OutputTarget != OutputTarget.VideoFile)
- {
- issues += "• Only output to video file is supported. Switching to video file output.\n";
- OutputTarget = OutputTarget.VideoFile;
- FilenameExtension = "mp4";
- GenerateFilename();
- }
- if (AudioCaptureSource != AudioCaptureSource.None && AudioCaptureSource != AudioCaptureSource.Unity)
- {
- issues += "• Audio source '" + AudioCaptureSource + "' not supported. Disabling audio capture.\n";
- AudioCaptureSource = AudioCaptureSource.None;
- }
- if (FrameRate != 30f)
- {
- issues += "• Frame rate '" + FrameRate + "' not supported. Switching to 30 FPS.\n";
- FrameRate = 30f;
- }
- if (GetEncoderHints().videoHints.supportTransparency || GetEncoderHints().videoHints.transparency != Transparency.None)
- {
- issues += "• Transparent capture not supported. Disabling transparent capture\n";
- GetEncoderHints().videoHints.supportTransparency = false;
- GetEncoderHints().videoHints.transparency = Transparency.None;
- _Transparency = Transparency.None;
- }
- }
- // Log/Display issues
- if (!string.IsNullOrEmpty(issues))
- {
- string message = "Limitations of Basic Edition reached:\n" + issues + "Please upgrade to use these feature, or visit '" + DocEditionsURL + "' for more information.";
- if (canContinueCapture)
- {
- Debug.LogWarning("[AVProMovieCapture] " + message);
- }
- else
- {
- Debug.LogError("[AVProMovieCapture] " + message);
- }
- #if UNITY_EDITOR
- message = "Limitations of Basic Edition reached:\n\n" + issues + "\nPlease upgrade to use these feature. View documention for more information.";
- if (!UnityEditor.EditorUtility.DisplayDialog("AVPro Movie Capture", message, "Ok", "More Info"))
- {
- Application.OpenURL(DocEditionsURL);
- }
- #endif
- }
- }
- return canContinueCapture;
- }
- public virtual bool PrepareCapture()
- {
- if (!ValidateEditionFeatures())
- {
- return false;
- }
- // Delete file if it already exists
- if (_outputTarget == OutputTarget.VideoFile && File.Exists(_filePath))
- {
- File.Delete(_filePath);
- }
- _stats = new CaptureStats();
- #if UNITY_EDITOR_WIN || (!UNITY_EDITOR && UNITY_STANDALONE_WIN)
- if (_minimumDiskSpaceMB > 0 && _outputTarget == OutputTarget.VideoFile)
- {
- ulong freespace = 0;
- if (Utils.DriveFreeBytes(System.IO.Path.GetPathRoot(_filePath), out freespace))
- {
- _freeDiskSpaceMB = (long)(freespace / (1024 * 1024));
- }
- if (!IsEnoughDiskSpace())
- {
- Debug.LogError("[AVProMovieCapture] Not enough free space to start capture. Stopping capture.");
- return false;
- }
- }
- #endif
- if (_isRealTime)
- {
- /*if (_allowFrameRateChange)
- {
- _oldTargetFrameRate = Application.targetFrameRate;
- Application.targetFrameRate = (int)_frameRate;
- }*/
- }
- else
- {
- // Disable vsync
- #if !UNITY_EDITOR && (UNITY_IOS || UNITY_ANDROID)
- if (_allowVSyncDisable)
- {
- // iOS and Android do not support disabling vsync so use _oldVsyncCount to store the current target framerate.
- _oldVSyncCount = Application.targetFrameRate;
- // We want to runs as fast as possible.
- Application.targetFrameRate = Screen.currentResolution.refreshRate;
- }
- #else
- if (_allowVSyncDisable && !Screen.fullScreen && QualitySettings.vSyncCount > 0)
- {
- _oldVSyncCount = QualitySettings.vSyncCount;
- QualitySettings.vSyncCount = 0;
- }
- #endif
- if (_useMotionBlur && _motionBlurSamples > 1)
- {
- #if AVPRO_MOVIECAPTURE_CAPTUREDELTA_SUPPORT
- Time.captureDeltaTime = 1f / (_motionBlurSamples * _frameRate);
- #else
- Time.captureFramerate = (int)(_motionBlurSamples * _frameRate);
- #endif
- // FromTexture and FromCamera360 captures don't require a camera for rendering, so set up the motion blur component differently
- if (this is CaptureFromTexture || this is CaptureFromCamera360 || this is CaptureFromCamera360ODS)
- {
- if (_motionBlur == null)
- {
- _motionBlur = this.GetComponent<MotionBlur>();
- }
- if (_motionBlur == null)
- {
- _motionBlur = this.gameObject.AddComponent<MotionBlur>();
- }
- if (_motionBlur != null)
- {
- _motionBlur.NumSamples = _motionBlurSamples;
- _motionBlur.SetTargetSize(_targetWidth, _targetHeight);
- _motionBlur.enabled = false;
- }
- }
- // FromCamera and FromScreen use this path
- else if (_motionBlurCameras.Length > 0)
- {
- // Setup the motion blur filters where cameras are used
- foreach (Camera camera in _motionBlurCameras)
- {
- MotionBlur mb = camera.GetComponent<MotionBlur>();
- if (mb == null)
- {
- mb = camera.gameObject.AddComponent<MotionBlur>();
- }
- if (mb != null)
- {
- mb.NumSamples = _motionBlurSamples;
- mb.enabled = true;
- _motionBlur = mb;
- }
- }
- }
- }
- else
- {
- #if AVPRO_MOVIECAPTURE_CAPTUREDELTA_SUPPORT
- Time.captureDeltaTime = 1f / _frameRate;
- #else
- Time.captureFramerate = (int)_frameRate;
- #endif
- }
- // Change physics update speed
- _oldFixedDeltaTime = Time.fixedDeltaTime;
- #if AVPRO_MOVIECAPTURE_CAPTUREDELTA_SUPPORT
- Time.fixedDeltaTime = Time.captureDeltaTime;
- #else
- Time.fixedDeltaTime = 1.0f / Time.captureFramerate;
- #endif
- }
- // Resolve desired audio source
- _stats.AudioCaptureSource = AudioCaptureSource.None;
- if (_audioCaptureSource != AudioCaptureSource.None && _outputTarget == OutputTarget.VideoFile)
- {
- if (_audioCaptureSource == AudioCaptureSource.Microphone && _isRealTime)
- {
- if (_selectedAudioInputDevice != null)
- {
- _stats.AudioCaptureSource = AudioCaptureSource.Microphone;
- }
- else
- {
- Debug.LogWarning("[AVProMovieCapture] No microphone found");
- }
- }
- else if (_audioCaptureSource == AudioCaptureSource.Unity || _audioCaptureSource == AudioCaptureSource.Wwise)
- {
- // If there is already a capture component, make sure it's the right one otherwise remove it
- if (_unityAudioCapture != null)
- {
- bool removeComponent = false;
- if (_audioCaptureSource == AudioCaptureSource.Unity)
- {
- if (_isRealTime)
- {
- removeComponent = !(_unityAudioCapture is CaptureAudioFromAudioListener);
- }
- else
- {
- removeComponent = (_unityAudioCapture is CaptureAudioFromAudioListener);
- }
- }
- else if (_audioCaptureSource == AudioCaptureSource.Wwise)
- {
- #if !AVPRO_MOVIECAPTURE_WWISE_SUPPORT
- removeComponent = true;
- #else
- if (_isRealTime)
- {
- removeComponent = true;
- }
- else
- {
- removeComponent = !(_unityAudioCapture is CaptureAudioFromWwise);
- }
- #endif
- }
- if (removeComponent)
- {
- Destroy(_unityAudioCapture);
- _unityAudioCapture = null;
- }
- }
- // We if try to capture audio from Unity but there isn't an UnityAudioCapture component set
- if (_unityAudioCapture == null)
- {
- _unityAudioCapture = FindOrCreateUnityAudioCapture(true);
- }
- if (_unityAudioCapture != null)
- {
- _unityAudioCapture.PrepareCapture();
- _stats.UnityAudioSampleRate = _unityAudioCapture.SampleRate;
- _stats.UnityAudioChannelCount = _unityAudioCapture.ChannelCount;
- _stats.AudioCaptureSource = _audioCaptureSource;
- }
- else
- {
- Debug.LogWarning("[AVProMovieCapture] Unable to create AudioCapture component in mode " + _audioCaptureSource.ToString());
- }
- }
- else if (_audioCaptureSource == AudioCaptureSource.UnityAudioMixer)
- {
- _stats.UnityAudioSampleRate = AudioSettings.outputSampleRate;
- _stats.UnityAudioChannelCount = UnityAudioCapture.GetUnityAudioChannelCount();
- _stats.AudioCaptureSource = _audioCaptureSource;
- }
- else if (_audioCaptureSource == AudioCaptureSource.Manual)
- {
- _stats.UnityAudioSampleRate = _manualAudioSampleRate;
- _stats.UnityAudioChannelCount = _manualAudioChannelCount;
- _stats.AudioCaptureSource = AudioCaptureSource.Manual;
- }
- }
- if (_selectedVideoCodec == null) return false;
- string info = string.Empty;
- if (_logCaptureStartStop)
- {
- info = string.Format("{0}x{1} @ {2}fps [{3}]", _targetWidth, _targetHeight, _frameRate.ToString("F2"), _pixelFormat.ToString());
- if (_outputTarget == OutputTarget.VideoFile)
- {
- info += string.Format(" vcodec:'{0}'", _selectedVideoCodec.Name);
- if (_stats.AudioCaptureSource != AudioCaptureSource.None)
- {
- if (_audioCaptureSource == AudioCaptureSource.Microphone && _selectedAudioInputDevice != null)
- {
- info += string.Format(" audio source:'{0}'", _selectedAudioInputDevice.Name);
- }
- else if (_audioCaptureSource == AudioCaptureSource.Unity && _unityAudioCapture != null)
- {
- info += string.Format(" audio source:'Unity' {0}hz {1} channels", _stats.UnityAudioSampleRate, _stats.UnityAudioChannelCount);
- }
- else if (_audioCaptureSource == AudioCaptureSource.UnityAudioMixer)
- {
- info += string.Format(" audio source:'AudioMixer' {0}hz {1} channels", _stats.UnityAudioSampleRate, _stats.UnityAudioChannelCount);
- }
- else if (_audioCaptureSource == AudioCaptureSource.Manual)
- {
- info += string.Format(" audio source:'Manual' {0}hz {1} channels", _stats.UnityAudioSampleRate, _stats.UnityAudioChannelCount);
- }
- else if (_audioCaptureSource == AudioCaptureSource.Wwise && _unityAudioCapture != null)
- {
- info += string.Format(" audio source:'Wwise' {0}hz {1} channels", _stats.UnityAudioSampleRate, _stats.UnityAudioChannelCount);
- }
- if (_selectedAudioCodec != null)
- {
- info += string.Format(" acodec:'{0}'", _selectedAudioCodec.Name);
- }
- }
- info += string.Format(" to file: '{0}'", _filePath);
- }
- else if (_outputTarget == OutputTarget.ImageSequence)
- {
- info += string.Format(" to file: '{0}'", _filePath);
- }
- else if (_outputTarget == OutputTarget.NamedPipe)
- {
- info += string.Format(" to pipe: '{0}'", _filePath);
- }
- }
- if (_outputTarget == OutputTarget.VideoFile)
- {
- if (_logCaptureStartStop)
- {
- Debug.Log("[AVProMovieCapture] Start File Capture: " + info);
- }
- bool useRealtimeClock = (_isRealTime && _timelapseScale <= 1);
- AudioCaptureSource audioCaptureSource = _stats.AudioCaptureSource;
- if (audioCaptureSource == AudioCaptureSource.Wwise) { audioCaptureSource = AudioCaptureSource.Unity; } // This is a mild hack until we rebuild the plugins
- VideoEncoderHints hints = GetEncoderHints().videoHints;
- hints.colourSpace = (VideoEncoderHints.ColourSpace)QualitySettings.activeColorSpace;
- hints.sourceWidth = _sourceWidth;
- hints.sourceHeight = _sourceHeight;
- // Android only
- hints.androidNoCaptureRotation = _androidNoCaptureRotation;
- hints.supportTransparency = (hints.transparency == Transparency.Codec);
- _handle = NativePlugin.CreateRecorderVideo(
- _filePath,
- (uint)_targetWidth,
- (uint)_targetHeight,
- _frameRate,
- (int)_pixelFormat,
- useRealtimeClock,
- #if !UNITY_EDITOR && UNITY_ANDROID
- _flipVertically,
- #else
- _flipVertically ? !_isTopDown : _isTopDown,
- #endif
- _selectedVideoCodec.Index,
- audioCaptureSource,
- _stats.UnityAudioSampleRate,
- _stats.UnityAudioChannelCount,
- (_selectedAudioInputDevice != null) ? _selectedAudioInputDevice.Index : -1,
- (_selectedAudioCodec != null) ? _selectedAudioCodec.Index : -1,
- _forceGpuFlush,
- hints);
- }
- else if (_outputTarget == OutputTarget.ImageSequence)
- {
- if (_logCaptureStartStop)
- {
- Debug.Log("[AVProMovieCapture] Start Images Capture: " + info);
- }
- bool useRealtimeClock = (_isRealTime && _timelapseScale <= 1);
- ImageEncoderHints hints = GetEncoderHints().imageHints;
- hints.colourSpace = (ImageEncoderHints.ColourSpace)QualitySettings.activeColorSpace;
- hints.sourceWidth = _sourceWidth;
- hints.sourceHeight = _sourceHeight;
- hints.supportTransparency = ( hints.transparency == Transparency.Codec );
- _handle = NativePlugin.CreateRecorderImages(
- _filePath,
- (uint)_targetWidth,
- (uint)_targetHeight,
- _frameRate,
- (int)_pixelFormat,
- useRealtimeClock,
- _isTopDown,
- (int)NativeImageSequenceFormat,
- _forceGpuFlush,
- _imageSequenceStartFrame,
- hints);
- }
- else if (_outputTarget == OutputTarget.NamedPipe)
- {
- if (_logCaptureStartStop)
- {
- Debug.Log("[AVProMovieCapture] Start Pipe Capture: " + info);
- }
- _handle = NativePlugin.CreateRecorderPipe(_filePath, (uint)_targetWidth, (uint)_targetHeight, _frameRate,
- (int)_pixelFormat, _isTopDown,
- /*GetEncoderHints().videoHints.supportTransparency*//*(GetEncoderHints().videoHints.transparency == Transparency.Codec)*/(int)(GetEncoderHints().videoHints.transparency),
- _forceGpuFlush);
- }
- if (_handle >= 0)
- {
- RenderThreadEvent(NativePlugin.PluginEvent.Setup);
- }
- else
- {
- Debug.LogError("[AVProMovieCapture] Failed to create recorder");
- // Try to give a reason why it failed
- #if UNITY_EDITOR_WIN || (!UNITY_EDITOR && UNITY_STANDALONE_WIN)
- if (_selectedVideoCodec.MediaApi == MediaApi.MediaFoundation)
- {
- if (!HasExtension(_filePath, ".mp4"))
- {
- Debug.LogError("[AVProMovieCapture] When using a MediaFoundation codec the MP4 extension must be used");
- }
- // MF H.264 encoder has a limit of Level 5.2 which is 9,437,184 luma pixels
- // but we've seen it fail slightly below this limit, so we test against 9360000
- // to offer a useful potential error message
- if (((_targetWidth * _targetHeight) >= 9360000) && _selectedVideoCodec.Name.Contains("H264"))
- {
- Debug.LogError("[AVProMovieCapture] Resolution is possibly too high for the MF H.264 codec");
- }
- }
- else if (_selectedVideoCodec.MediaApi == MediaApi.DirectShow)
- {
- if (HasExtension(_filePath, ".mp4") && _selectedVideoCodec.Name.Contains("Uncompressed"))
- {
- Debug.LogError("[AVProMovieCapture] Uncompressed video codec not supported with MP4 extension, use AVI instead for uncompressed");
- }
- }
- #endif
- StopCapture();
- }
- // (mac|i)OS only for now
- #if UNITY_EDITOR_OSX || (!UNITY_EDITOR && (UNITY_STANDALONE_OSX || UNITY_IOS))
- SetupErrorHandler();
- #endif
- return (_handle >= 0);
- }
- #if UNITY_EDITOR_OSX || (!UNITY_EDITOR && (UNITY_STANDALONE_OSX || UNITY_IOS))
- static Dictionary<int, CaptureBase> _HandleToCaptureMap = new Dictionary<int, CaptureBase>();
- private void SetupErrorHandler()
- {
- NativePlugin.ErrorHandlerDelegate errorHandlerDelegate = new NativePlugin.ErrorHandlerDelegate(ErrorHandler);
- System.IntPtr func = Marshal.GetFunctionPointerForDelegate(errorHandlerDelegate);
- NativePlugin.SetErrorHandler(_handle, func);
- _HandleToCaptureMap.Add(_handle, this);
- }
- private void CleanupErrorHandler()
- {
- _HandleToCaptureMap.Remove(_handle);
- }
- #if ENABLE_IL2CPP
- [MonoPInvokeCallback(typeof(NativePlugin.ErrorHandlerDelegate))]
- #endif
- private static void ErrorHandler(int handle, int domain, int code, string message)
- {
- CaptureBase capture;
- if (_HandleToCaptureMap.TryGetValue(handle, out capture))
- {
- capture.ActualErrorHandler(domain, code, message);
- }
- }
- private void ActualErrorHandler(int domain, int code, string message) {
- if (_capturing)
- {
- CancelCapture();
- Debug.LogError("Capture cancelled");
- }
- Debug.LogErrorFormat("Error: domain: {0}, code: {1}, message: {2}", domain, code, message);
- }
- #endif
- public void QueueStartCapture()
- {
- _queuedStartCapture = true;
- _stats = new CaptureStats();
- }
- public bool IsStartCaptureQueued()
- {
- return _queuedStartCapture;
- }
- protected void UpdateInjectionOptions(StereoPacking stereoPacking, SphericalVideoLayout sphericalVideoLayout)
- {
- VideoEncoderHints videoHints = GetEncoderHints().videoHints;
- if (videoHints.injectStereoPacking == NoneAutoCustom.Auto) { videoHints.stereoPacking = stereoPacking; }
- if (videoHints.injectSphericalVideoLayout == NoneAutoCustom.Auto) { videoHints.sphericalVideoLayout = sphericalVideoLayout; }
- }
- public bool StartCapture()
- {
- if (_capturing)
- {
- return false;
- }
- if (_waitForEndOfFrame == null)
- {
- // Start() hasn't happened yet, so queue the StartCapture
- QueueStartCapture();
- return false;
- }
- if (_handle < 0)
- {
- if (!PrepareCapture())
- {
- return false;
- }
- }
- if (_handle >= 0)
- {
- if (IsUsingUnityAudioComponent())
- {
- _unityAudioCapture.StartCapture();
- }
- // Set limit to number of frames encoded (or 0 for unlimited)
- {
- uint frameLimit = 0;
- if (_stopMode == StopMode.FramesEncoded)
- {
- frameLimit = (uint)_stopFrames;
- }
- else if (_stopMode == StopMode.SecondsEncoded && !_isRealTime)
- {
- frameLimit = (uint)Mathf.FloorToInt(_stopSeconds * _frameRate);
- }
- NativePlugin.SetEncodedFrameLimit(_handle, frameLimit);
- }
- if (!NativePlugin.Start(_handle))
- {
- StopCapture(true);
- Debug.LogError("[AVProMovieCapture] Failed to start recorder");
- return false;
- }
- ResetFPS();
- _captureStartTime = Time.realtimeSinceStartup;
- _capturePrePauseTotalTime = 0f;
- // NOTE: We set this to the elapsed time so that the first frame is captured immediately
- _timeSinceLastFrame = GetSecondsPerCaptureFrame();
- #if AVPRO_MOVIECAPTURE_PLAYABLES_SUPPORT
- if (!_isRealTime && _timelineController != null)
- {
- _timelineController.StartCapture();
- }
- #endif
- #if AVPRO_MOVIECAPTURE_VIDEOPLAYER_SUPPORT
- if (!_isRealTime && _videoPlayerController != null)
- {
- _videoPlayerController.StartCapture();
- }
- #endif
- _capturing = true;
- _paused = false;
- if (_startDelay != StartDelayMode.None)
- {
- _startDelayTimer = 0f;
- _startPaused = true;
- PauseCapture();
- }
- #if UNITY_EDITOR
- if (UnityEditor.EditorApplication.isPaused)
- {
- PauseCapture();
- }
- #endif
- }
- return _capturing;
- }
- public void PauseCapture()
- {
- if (_capturing && !_paused)
- {
- if (IsUsingUnityAudioComponent())
- {
- _unityAudioCapture.enabled = false;
- }
- NativePlugin.Pause(_handle);
- if (!_isRealTime)
- {
- // TODO: should be store the timeScale value and restore it instead of assuming timeScale == 1.0?
- Time.timeScale = 0f;
- }
- _paused = true;
- ResetFPS();
- }
- }
- public void ResumeCapture()
- {
- if (_capturing && _paused)
- {
- if (IsUsingUnityAudioComponent())
- {
- _unityAudioCapture.FlushBuffer();
- _unityAudioCapture.enabled = true;
- }
- NativePlugin.Start(_handle);
- if (!_isRealTime)
- {
- Time.timeScale = 1f;
- }
- _paused = false;
- if (_startPaused)
- {
- _captureStartTime = Time.realtimeSinceStartup;
- _capturePrePauseTotalTime = 0f;
- _startPaused = false;
- }
- }
- }
- public void CancelCapture()
- {
- StopCapture(true, false, true);
- }
- public static void DeleteCapture(OutputTarget outputTarget, string path)
- {
- try
- {
- if (outputTarget == OutputTarget.VideoFile && File.Exists(path))
- {
- File.Delete(path);
- }
- else if (outputTarget == OutputTarget.ImageSequence)
- {
- string directory = Path.GetDirectoryName(path);
- if (Directory.Exists(directory))
- {
- Directory.Delete(directory, true);
- }
- }
- }
- catch (Exception ex)
- {
- Debug.LogWarning("[AVProMovieCapture] Failed to delete capture - " + ex.Message);
- }
- }
- public virtual void UnprepareCapture()
- {
- #if UNITY_EDITOR_OSX || (!UNITY_EDITOR && (UNITY_STANDALONE_OSX || UNITY_IOS))
- CleanupErrorHandler();
- #endif
- }
- public static string LastFileSaved
- {
- get
- {
- #if UNITY_EDITOR
- return UnityEditor.EditorPrefs.GetString("AVProMovieCapture-LastSavedFile", string.Empty);
- #else
- return PlayerPrefs.GetString("AVProMovieCapture-LastSavedFile", string.Empty);
- #endif
- }
- set
- {
- PlayerPrefs.SetString("AVProMovieCapture-LastSavedFile", value);
- #if UNITY_EDITOR
- UnityEditor.EditorPrefs.SetString("AVProMovieCapture-LastSavedFile", value);
- #endif
- }
- }
- protected void RenderThreadEvent(NativePlugin.PluginEvent renderEvent)
- {
- NativePlugin.RenderThreadEvent(renderEvent, _handle);
- }
- public virtual void StopCapture(bool skipPendingFrames = false, bool ignorePendingFileWrites = false, bool deleteCapture = false)
- {
- if (_capturing)
- {
- if (_logCaptureStartStop)
- {
- if (!deleteCapture)
- {
- Debug.Log("[AVProMovieCapture] Stopping capture " + _handle);
- }
- else
- {
- Debug.Log("[AVProMovieCapture] Canceling capture " + _handle);
- }
- }
- _capturing = false;
- }
- bool applyPostOperations = false;
- FileWritingHandler fileWritingHandler = null;
- if (_handle >= 0)
- {
- NativePlugin.Stop(_handle, skipPendingFrames);
- UnprepareCapture();
- if (_outputTarget == OutputTarget.VideoFile)
- {
- applyPostOperations = true;
- }
- #if UNITY_ANDROID && !UNITY_EDITOR
- bool updateMediaGallery = ( _androidUpdateMediaGallery && ( _outputFolderType == OutputPath.RelativeToPictures || _outputFolderType == OutputPath.RelativeToVideos || _outputFolderType == OutputPath.PhotoLibrary ) );
- #else
- bool updateMediaGallery = ( _outputFolderType == OutputPath.RelativeToPictures || _outputFolderType == OutputPath.RelativeToVideos || _outputFolderType == OutputPath.PhotoLibrary );
- #endif
- fileWritingHandler = new FileWritingHandler(_outputTarget, _filePath, _handle, deleteCapture, _finalFilePath, updateMediaGallery);
- if (_completedFileWritingAction != null)
- {
- fileWritingHandler.CompletedFileWritingAction = _completedFileWritingAction;
- }
- // Free the recorder, or if the file is still being written, store the action to be invoked where it is complete
- bool canFreeRecorder = (ignorePendingFileWrites || NativePlugin.IsFileWritingComplete(_handle));
- if (canFreeRecorder)
- {
- // If there is an external action set up, then notify it that writing has begun
- if (_beginFinalFileWritingAction != null)
- {
- _beginFinalFileWritingAction.Invoke(fileWritingHandler);
- }
- // Complete writing immediately
- fileWritingHandler.Dispose();
- fileWritingHandler = null;
- }
- else
- {
- // If no external action has been set up for the checking when the file writing begins and end,
- // add it to an internal list so we can make sure it completes
- if (_beginFinalFileWritingAction == null)
- {
- _pendingFileWrites.Add(fileWritingHandler);
- }
- if (!deleteCapture)
- {
- VideoEncoderHints hints = GetEncoderHints().videoHints;
- if (applyPostOperations && CanApplyPostOperations(_filePath, hints, _finalFilePath))
- {
- MP4FileProcessing.Options options = CreatePostOperationsOptions(hints, _finalFilePath);
- fileWritingHandler.SetFilePostProcess(options);
- }
- }
- applyPostOperations = false;
- }
- // If there is an external action set up, then notify it that writing has begun
- if (_beginFinalFileWritingAction != null && fileWritingHandler != null)
- {
- _beginFinalFileWritingAction.Invoke(fileWritingHandler);
- }
- _handle = -1;
- // Save the last captured path
- if (!deleteCapture)
- {
- if (!string.IsNullOrEmpty(_filePath))
- {
- if (_outputTarget == OutputTarget.VideoFile)
- {
- LastFileSaved = _filePath;
- }
- else if (_outputTarget == OutputTarget.ImageSequence)
- {
- LastFileSaved = System.IO.Path.GetDirectoryName(_filePath);
- }
- }
- }
- #if AVPRO_MOVIECAPTURE_VIDEOPLAYER_SUPPORT
- if (_videoPlayerController != null)
- {
- _videoPlayerController.StopCapture();
- }
- #endif
- #if AVPRO_MOVIECAPTURE_PLAYABLES_SUPPORT
- if (_timelineController != null)
- {
- _timelineController.StopCapture();
- }
- #endif
- }
- _fileInfo = null;
- if (_unityAudioCapture)
- {
- _unityAudioCapture.StopCapture();
- }
- if (_motionBlur)
- {
- _motionBlur.enabled = false;
- }
- // Restore Unity timing
- Time.captureFramerate = 0;
- //Application.targetFrameRate = _oldTargetFrameRate;
- //_oldTargetFrameRate = -1;
- if (_oldFixedDeltaTime > 0f)
- {
- Time.fixedDeltaTime = _oldFixedDeltaTime;
- }
- _oldFixedDeltaTime = 0f;
- #if !UNITY_EDITOR_OSX && (UNITY_IOS || UNITY_ANDROID)
- // Android and iOS do not support disabling vsync so _oldVsyncCount is actually the target framerate before we started capturing.
- if (_oldVSyncCount != 0)
- {
- Application.targetFrameRate = _oldVSyncCount;
- _oldVSyncCount = 0;
- }
- #else
- if (_oldVSyncCount > 0)
- {
- QualitySettings.vSyncCount = _oldVSyncCount;
- _oldVSyncCount = 0;
- }
- #endif
- _motionBlur = null;
- if (_texture != null)
- {
- Destroy(_texture);
- _texture = null;
- }
- if( _sideBySideMaterial != null )
- {
- Destroy( _sideBySideMaterial );
- _sideBySideMaterial = null;
- }
- if( _sideBySideTexture != null )
- {
- RenderTexture.ReleaseTemporary( _sideBySideTexture );
- _sideBySideTexture = null;
- }
- if (applyPostOperations)
- {
- ApplyPostOperations(_filePath, GetEncoderHints().videoHints, _finalFilePath);
- }
- }
- private static MP4FileProcessing.Options CreatePostOperationsOptions(VideoEncoderHints hints, string finalFilePath)
- {
- MP4FileProcessing.Options options = new MP4FileProcessing.Options();
- #if UNITY_EDITOR_WIN || (!UNITY_EDITOR && (UNITY_STANDALONE_WIN || UNITY_ANDROID))
- // macOS and iOS don't require fast start postprocess as it is handled internally
- options.applyFastStart = hints.allowFastStartStreamingPostProcess;
- #endif
- options.applyStereoMode = (hints.injectStereoPacking != NoneAutoCustom.None) && (hints.stereoPacking != StereoPacking.None);
- if (options.applyStereoMode)
- {
- options.stereoMode = hints.stereoPacking;
- }
- options.applySphericalVideoLayout = (hints.injectSphericalVideoLayout != NoneAutoCustom.None) && (hints.sphericalVideoLayout != SphericalVideoLayout.None);
- if (options.applySphericalVideoLayout)
- {
- options.sphericalVideoLayout = hints.sphericalVideoLayout;
- }
- options.applyMoveCaptureFile = (finalFilePath != null);
- if(options.applyMoveCaptureFile)
- {
- options.finalCaptureFilePath = finalFilePath;
- }
- return options;
- }
- private static bool CanApplyPostOperations(string filePath, VideoEncoderHints hints, string finalFilePath)
- {
- bool result = false;
- if (HasExtension(filePath, ".mp4") || HasExtension(filePath, ".mov") && File.Exists(filePath))
- {
- result = CreatePostOperationsOptions(hints, finalFilePath).HasOptions();
- }
- return result;
- }
- protected void ApplyPostOperations(string filePath, VideoEncoderHints hints, string finalFilePath)
- {
- if (CanApplyPostOperations(filePath, hints, finalFilePath))
- {
- try
- {
- MP4FileProcessing.Options options = CreatePostOperationsOptions(hints, finalFilePath);
- if (!MP4FileProcessing.ProcessFile(filePath, false, options))
- {
- Debug.LogWarning("[AVProMovieCapture] failed to postprocess file: " + filePath);
- }
- }
- catch (System.Exception e)
- {
- Debug.LogException(e);
- }
- }
- }
- private void ToggleCapture()
- {
- if (_capturing)
- {
- //_queuedStopCapture = true;
- //_queuedStartCapture = false;
- StopCapture();
- }
- else
- {
- //_queuedStartCapture = true;
- //_queuedStopCapture = false;
- StartCapture();
- }
- }
- private bool IsEnoughDiskSpace()
- {
- bool result = true;
- #if UNITY_EDITOR_WIN || (!UNITY_EDITOR && UNITY_STANDALONE_WIN)
- long fileSizeMB = GetCaptureFileSize() / (1024 * 1024);
- if ((_freeDiskSpaceMB - fileSizeMB) < _minimumDiskSpaceMB)
- {
- result = false;
- }
- #endif
- return result;
- }
- protected bool CanContinue()
- {
- bool result = true;
- #if AVPRO_MOVIECAPTURE_VIDEOPLAYER_SUPPORT
- if (IsCapturing() && !IsPaused() && !_isRealTime && _videoPlayerController != null)
- {
- result = _videoPlayerController.CanContinue();
- }
- #endif
- return result;
- }
- private void Update()
- {
- if (_queuedStopCapture)
- {
- _queuedStopCapture = _queuedStartCapture = false;
- StopCapture(false, false);
- }
- if (_queuedStartCapture)
- {
- _queuedStopCapture = _queuedStartCapture = false;
- StartCapture();
- }
- }
- private void LateUpdate()
- {
- if (_handle >= 0 && !_paused)
- {
- CheckFreeDiskSpace();
- }
- if (_captureKey != KeyCode.None)
- {
- #if (!ENABLE_INPUT_SYSTEM || ENABLE_LEGACY_INPUT_MANAGER)
- if (Input.GetKeyDown(_captureKey))
- {
- ToggleCapture();
- }
- #endif
- }
- RemoveCompletedFileWrites();
- if (_frameUpdateMode == FrameUpdateMode.Automatic)
- {
- // Resume capture if a start delay has been specified
- if (IsCapturing() && IsPaused() && _stats.NumEncodedFrames == 0)
- {
- float delta = 0f;
- if (_startDelay == StartDelayMode.GameSeconds)
- {
- if (!_isRealTime)
- {
- // In offline render mode Time.deltaTime is always zero due Time.timeScale being set to zero,
- // so just use the real world time
- delta = Time.unscaledDeltaTime;
- }
- else
- {
- delta = Time.deltaTime;
- }
- }
- else if (_startDelay == StartDelayMode.RealSeconds)
- {
- delta = Time.unscaledDeltaTime;
- }
- if (delta > 0f)
- {
- _startDelayTimer += delta;
- if (IsStartDelayComplete())
- {
- ResumeCapture();
- }
- }
- }
- PreUpdateFrame();
- UpdateFrame();
- }
- }
- private void RemoveCompletedFileWrites()
- {
- for (int i = _pendingFileWrites.Count - 1; i >= 0; i--)
- {
- FileWritingHandler handler = _pendingFileWrites[i];
- if (handler.IsFileReady())
- {
- _pendingFileWrites.RemoveAt(i);
- }
- }
- }
- private void CheckFreeDiskSpace()
- {
- if (_minimumDiskSpaceMB > 0)
- {
- if (!IsEnoughDiskSpace())
- {
- Debug.LogWarning("[AVProMovieCapture] Free disk space getting too low. Stopping capture.");
- StopCapture(true);
- }
- }
- }
- protected bool IsStartDelayComplete()
- {
- bool result = false;
- if (_startDelay == StartDelayMode.None)
- {
- result = true;
- }
- else if (_startDelay == StartDelayMode.GameSeconds ||
- _startDelay == StartDelayMode.RealSeconds)
- {
- result = (_startDelayTimer >= _startDelaySeconds);
- }
- return result;
- }
- protected bool IsStopTimeReached()
- {
- bool result = false;
- if (_stopMode != StopMode.None)
- {
- switch (_stopMode)
- {
- case StopMode.FramesEncoded:
- result = (_stats.NumEncodedFrames >= _stopFrames);
- break;
- case StopMode.SecondsEncoded:
- if (!_isRealTime)
- {
- // In non-realtime mode this is a more accurate way to determine encoded time
- result = (_stats.NumEncodedFrames >= _stopSeconds * _frameRate);
- }
- else
- {
- result = (_stats.TotalEncodedSeconds >= _stopSeconds);
- }
- break;
- case StopMode.SecondsElapsed:
- if (!_startPaused && !_paused)
- {
- float timeSinceLastEditorPause = (Time.realtimeSinceStartup - _captureStartTime);
- result = (timeSinceLastEditorPause + _capturePrePauseTotalTime) >= _stopSeconds;
- }
- break;
- }
- }
- return result;
- }
- public float GetProgress()
- {
- float result = 0f;
- if (_stopMode != StopMode.None)
- {
- switch (_stopMode)
- {
- case StopMode.FramesEncoded:
- result = (_stats.NumEncodedFrames / (float)_stopFrames);
- break;
- case StopMode.SecondsEncoded:
- result = ((_stats.NumEncodedFrames / _frameRate) / _stopSeconds);
- break;
- case StopMode.SecondsElapsed:
- if (!_startPaused && !_paused)
- {
- float timeSinceLastEditorPause = (Time.realtimeSinceStartup - _captureStartTime);
- result = (timeSinceLastEditorPause + _capturePrePauseTotalTime) / _stopSeconds;
- }
- break;
- }
- }
- return result;
- }
- protected float GetSecondsPerCaptureFrame()
- {
- float timelapseScale = (float)_timelapseScale;
- if (!_isRealTime)
- {
- timelapseScale = 1f;
- }
- float captureFrameRate = _frameRate / timelapseScale;
- float secondsPerFrame = 1f / captureFrameRate;
- return secondsPerFrame;
- }
- protected bool CanOutputFrame()
- {
- bool result = false;
- if (_handle >= 0)
- {
- if (_isRealTime)
- {
- if (NativePlugin.IsNewFrameDue(_handle))
- {
- result = (_timeSinceLastFrame >= GetSecondsPerCaptureFrame());
- //result = true;
- }
- }
- else
- {
- const int WatchDogLimit = 1000;
- int watchdog = 0;
- if (_outputTarget != OutputTarget.NamedPipe)
- {
- // Wait for the encoder to have an available buffer
- // The watchdog prevents an infinite while loop
- while (_handle >= 0 && !NativePlugin.IsNewFrameDue(_handle) && watchdog < WatchDogLimit)
- {
- System.Threading.Thread.Sleep(1);
- watchdog++;
- }
- }
- // Return handle status as it may have closed elsewhere
- result = (_handle >= 0) && (watchdog < WatchDogLimit);
- }
- }
- return result;
- }
- protected void TickFrameTimer()
- {
- _timeSinceLastFrame += Time.deltaTime;//unscaledDeltaTime;
- }
- protected void RenormTimer()
- {
- float secondsPerFrame = GetSecondsPerCaptureFrame();
- if (_timeSinceLastFrame >= secondsPerFrame)
- {
- _timeSinceLastFrame -= secondsPerFrame;
- }
- }
- public virtual Texture GetPreviewTexture()
- {
- return null;
- }
- public virtual Texture GetSideBySideTexture()
- {
- return _sideBySideTexture;
- }
- protected void EncodeUnityAudio()
- {
- if (IsUsingUnityAudioComponent())
- {
- int audioDataLength = 0;
- System.IntPtr audioDataPtr = _unityAudioCapture.ReadData(out audioDataLength);
- if (audioDataLength > 0)
- {
- NativePlugin.EncodeAudio(_handle, audioDataPtr, (uint)audioDataLength);
- }
- }
- }
- public void EncodeAudio(NativeArray<float> audioData)
- {
- if (audioData.Length > 0)
- {
- #if UNITY_NATIVEARRAY_UNSAFE_SUPPORT
- unsafe
- {
- System.IntPtr pointer = (System.IntPtr)Unity.Collections.LowLevel.Unsafe.NativeArrayUnsafeUtility.GetUnsafeReadOnlyPtr(audioData);
- NativePlugin.EncodeAudio(_handle, pointer, (uint)audioData.Length);
- }
- #else
- EncodeAudio(audioData.ToArray());
- #endif
- }
- }
- public void EncodeAudio(float[] audioData)
- {
- if (audioData.Length > 0)
- {
- int byteCount = Marshal.SizeOf(audioData[0]) * audioData.Length;
- // Copy the array to unmanaged memory.
- System.IntPtr pointer = Marshal.AllocHGlobal(byteCount);
- Marshal.Copy(audioData, 0, pointer, audioData.Length);
- // Encode
- NativePlugin.EncodeAudio(_handle, pointer, (uint)audioData.Length);
- // Free the unmanaged memory.
- Marshal.FreeHGlobal(pointer);
- }
- }
- public virtual void PreUpdateFrame()
- {
- #if AVPRO_MOVIECAPTURE_PLAYABLES_SUPPORT
- if (IsCapturing() && !IsPaused() && !_isRealTime && _timelineController != null)
- {
- _timelineController.UpdateFrame();
- }
- #endif
- #if AVPRO_MOVIECAPTURE_VIDEOPLAYER_SUPPORT
- if (IsCapturing() && !IsPaused() && !_isRealTime && _videoPlayerController != null)
- {
- _videoPlayerController.UpdateFrame();
- }
- #endif
- }
- public virtual void UpdateFrame()
- {
- // NOTE: Unlike other CaptureFrom components, CaptureFromScreen uses a coroutine, so when it calls base.UpdateFrame() is could still process the frame afterwards
- if (_handle >= 0 && !_paused)
- {
- _stats.NumDroppedFrames = NativePlugin.GetNumDroppedFrames(_handle);
- _stats.NumDroppedEncoderFrames = NativePlugin.GetNumDroppedEncoderFrames(_handle);
- _stats.NumEncodedFrames = NativePlugin.GetNumEncodedFrames(_handle);
- _stats.TotalEncodedSeconds = NativePlugin.GetEncodedSeconds(_handle);
- if (IsStopTimeReached())
- {
- _queuedStopCapture = true;
- }
- }
- }
- protected bool InitialiseSideBySideTransparency( int width, int height, bool screenFlip = false, int antiAliasing = 1 )
- {
- bool reInitialised = false;
- if( _Transparency == Transparency.LeftRight || _Transparency == Transparency.TopBottom )
- {
- if( !_sideBySideMaterial )
- {
- _sideBySideMaterial = new Material(Shader.Find("Hidden/AVProMovieCapture/SideBySideAlpha"));
- switch (_Transparency)
- {
- case Transparency.TopBottom: _sideBySideMaterial.DisableKeyword("ALPHA_LEFT_RIGHT"); _sideBySideMaterial.EnableKeyword("ALPHA_TOP_BOTTOM"); break;
- case Transparency.LeftRight: _sideBySideMaterial.EnableKeyword("ALPHA_LEFT_RIGHT"); _sideBySideMaterial.DisableKeyword("ALPHA_TOP_BOTTOM"); break;
- }
- if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Metal ||
- SystemInfo.graphicsDeviceType == GraphicsDeviceType.Vulkan ||
- SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11 ||
- SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D12)
- {
- _sideBySideMaterial.DisableKeyword( screenFlip ? "FLIPPED" : "SCREEN_FLIPPED" );
- _sideBySideMaterial.EnableKeyword( screenFlip ? "SCREEN_FLIPPED" : "FLIPPED" );
- }
- }
- int createWidth = width;
- int createHeight = height;
- switch( _Transparency )
- {
- case Transparency.TopBottom: createHeight *= 2; break;
- case Transparency.LeftRight: createWidth *= 2; break;
- }
- if( !_sideBySideTexture || (createWidth != _sideBySideTexture.width || createHeight != _sideBySideTexture.height) )
- {
- if( _sideBySideTexture )
- {
- RenderTexture.ReleaseTemporary( _sideBySideTexture );
- _sideBySideTexture = null;
- }
- _sideBySideTexture = RenderTexture.GetTemporary(createWidth,
- createHeight,
- 0,
- RenderTextureFormat.ARGB32,
- RenderTextureReadWrite.sRGB, // Is this correct for gamma colourspace?
- antiAliasing);
- _sideBySideTexture.name = "[AVProMovieCapture] SideBySide Transparency Target";
- _sideBySideTexture.Create();
- reInitialised = true;
- }
- }
- return reInitialised;
- }
- protected RenderTexture UpdateForSideBySideTransparency( Texture sourceTexture, bool screenFlip = false, int antiAliasing = 1)
- {
- if( sourceTexture )
- {
- // Ensure things are setup
- InitialiseSideBySideTransparency( sourceTexture.width, sourceTexture.height, screenFlip, antiAliasing );
- if ( _sideBySideTexture )
- {
- _sideBySideTexture.DiscardContents();
- if( sourceTexture && _sideBySideMaterial )
- {
- Graphics.Blit( sourceTexture, _sideBySideTexture, _sideBySideMaterial );
- }
- }
- }
- return _sideBySideTexture;
- }
- protected void ResetFPS()
- {
- _stats.ResetFPS();
- }
- public void UpdateFPS()
- {
- _stats.UpdateFPS();
- }
- protected int GetCameraAntiAliasingLevel(Camera camera)
- {
- int aaLevel = QualitySettings.antiAliasing;
- if (aaLevel == 0)
- {
- aaLevel = 1;
- }
- if (_renderAntiAliasing > 0)
- {
- aaLevel = _renderAntiAliasing;
- }
- if (aaLevel != 1 && aaLevel != 2 && aaLevel != 4 && aaLevel != 8)
- {
- Debug.LogWarning("[AVProMovieCapture] Invalid antialiasing value, must be 1, 2, 4 or 8. Defaulting to 1. >> " + aaLevel);
- aaLevel = 1;
- }
- if (aaLevel != 1)
- {
- if (camera.actualRenderingPath == RenderingPath.DeferredLighting || camera.actualRenderingPath == RenderingPath.DeferredShading)
- {
- Debug.LogWarning("[AVProMovieCapture] Not using antialiasing because MSAA is not supported by camera render path " + camera.actualRenderingPath);
- aaLevel = 1;
- }
- }
- return aaLevel;
- }
- public long GetCaptureFileSize()
- {
- long result = 0;
- #if UNITY_EDITOR_OSX || (!UNITY_EDITOR && (UNITY_STANDALONE_OSX || UNITY_IOS || UNITY_ANDROID))
- result = NativePlugin.GetFileSize(_handle);
- #elif !UNITY_WEBPLAYER
- if (_handle >= 0 && _outputTarget == OutputTarget.VideoFile)
- {
- if (_fileInfo == null && File.Exists(_filePath))
- {
- _fileInfo = new System.IO.FileInfo(_filePath);
- }
- if (_fileInfo != null)
- {
- _fileInfo.Refresh();
- result = _fileInfo.Length;
- }
- }
- #endif
- return result;
- }
- public static void GetResolution(Resolution res, ref int width, ref int height)
- {
- switch (res)
- {
- case Resolution.POW2_8192x8192:
- width = 8192; height = 8192;
- break;
- case Resolution.POW2_8192x4096:
- width = 8192; height = 4096;
- break;
- case Resolution.POW2_4096x4096:
- width = 4096; height = 4096;
- break;
- case Resolution.POW2_4096x2048:
- width = 4096; height = 2048;
- break;
- case Resolution.POW2_2048x4096:
- width = 2048; height = 4096;
- break;
- case Resolution.UHD_3840x2160:
- width = 3840; height = 2160;
- break;
- case Resolution.UHD_3840x2048:
- width = 3840; height = 2048;
- break;
- case Resolution.UHD_3840x1920:
- width = 3840; height = 1920;
- break;
- case Resolution.UHD_2560x1440:
- width = 2560; height = 1440;
- break;
- case Resolution.POW2_2048x2048:
- width = 2048; height = 2048;
- break;
- case Resolution.POW2_2048x1024:
- width = 2048; height = 1024;
- break;
- case Resolution.HD_1920x1080:
- width = 1920; height = 1080;
- break;
- case Resolution.HD_1280x720:
- width = 1280; height = 720;
- break;
- case Resolution.SD_1024x768:
- width = 1024; height = 768;
- break;
- case Resolution.SD_800x600:
- width = 800; height = 600;
- break;
- case Resolution.SD_800x450:
- width = 800; height = 450;
- break;
- case Resolution.SD_640x480:
- width = 640; height = 480;
- break;
- case Resolution.SD_640x360:
- width = 640; height = 360;
- break;
- case Resolution.SD_320x240:
- width = 320; height = 240;
- break;
- }
- }
- // Returns the next multiple of 4 or the same value if it's already a multiple of 4
- protected static int NextMultipleOf4(int value)
- {
- return (value + 3) & ~0x03;
- }
- // Audio capture support
- /// <summary>
- /// Authorisation for audio capture.
- /// </summary>
- public enum AudioCaptureDeviceAuthorisationStatus
- {
- /// <summary>Audio capture is unavailable.</summary>
- Unavailable = -1,
- /// <summary>Authorisation is still to be requested.</summary>
- NotDetermined,
- /// <summary>Authorisation has been denied.</summary>
- Denied,
- /// <summary>Authorisation has been granted.</summary>
- Authorised
- };
- /// <summary>
- /// Check to see if authorisation has been given to capture audio.
- /// </summary>
- private static bool _waitingForAudioCaptureDeviceAuthorisation = true;
- private static bool _hasCheckedAudioCaptureDeviceAuthorisationStatus = false;
- private static AudioCaptureDeviceAuthorisationStatus _audioCaptureDeviceAuthorisationStatus = AudioCaptureDeviceAuthorisationStatus.NotDetermined;
- public static AudioCaptureDeviceAuthorisationStatus HasUserAuthorisationToCaptureAudio()
- {
- if (!_hasCheckedAudioCaptureDeviceAuthorisationStatus || _audioCaptureDeviceAuthorisationStatus == AudioCaptureDeviceAuthorisationStatus.NotDetermined)
- {
- _hasCheckedAudioCaptureDeviceAuthorisationStatus = true;
- #if !UNITY_EDITOR_OSX && (UNITY_STANDALONE_OSX || (UNITY_IOS && !UNITY_EDITOR))
- _audioCaptureDeviceAuthorisationStatus = (AudioCaptureDeviceAuthorisationStatus)NativePlugin.AudioCaptureDeviceAuthorisationStatus();
- #elif UNITY_ANDROID && !UNITY_EDITOR
- _audioCaptureDeviceAuthorisationStatus = Permission.HasUserAuthorizedPermission(Permission.Microphone) ? AudioCaptureDeviceAuthorisationStatus.Authorised
- : AudioCaptureDeviceAuthorisationStatus.NotDetermined;
- #else
- _audioCaptureDeviceAuthorisationStatus = AudioCaptureDeviceAuthorisationStatus.Unavailable;
- #endif
- }
- return _audioCaptureDeviceAuthorisationStatus;
- }
- #if ENABLE_IL2CPP && (UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX || (UNITY_IOS && !UNITY_EDITOR))
- [MonoPInvokeCallback(typeof(NativePlugin.RequestAudioCaptureDeviceAuthorisationDelegate))]
- #endif
- private static void RequestUserAuthorisationToCaptureAudioCallback(int authorisation)
- {
- _audioCaptureDeviceAuthorisationStatus = (AudioCaptureDeviceAuthorisationStatus)authorisation;
- _hasCheckedAudioCaptureDeviceAuthorisationStatus = true;
- _waitingForAudioCaptureDeviceAuthorisation = false;
- }
- private class WaitForAudioCaptureDeviceAuthorisation : CustomYieldInstruction
- {
- public override bool keepWaiting { get { return CaptureBase._waitingForAudioCaptureDeviceAuthorisation; } }
- }
- /// <summary>
- /// Request authorisation to capture audio.
- /// </summary>
- public static CustomYieldInstruction RequestAudioCaptureDeviceUserAuthorisation()
- {
- #if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX || (UNITY_IOS && !UNITY_EDITOR)
- NativePlugin.RequestAudioCaptureDeviceAuthorisationDelegate callback = new NativePlugin.RequestAudioCaptureDeviceAuthorisationDelegate(RequestUserAuthorisationToCaptureAudioCallback);
- System.IntPtr ptr = Marshal.GetFunctionPointerForDelegate(callback);
- NativePlugin.RequestAudioCaptureDeviceAuthorisation(ptr);
- return new WaitForAudioCaptureDeviceAuthorisation();
- #elif UNITY_ANDROID && !UNITY_EDITOR
- Permission.RequestUserPermission(Permission.Microphone);
- RequestUserAuthorisationToCaptureAudioCallback((int)HasUserAuthorisationToCaptureAudio());
- return new WaitForAudioCaptureDeviceAuthorisation();
- #else
- return null;
- #endif
- }
- // Photo library support
- /// <summary>
- /// Level of access for the photos library.
- ///</summary>
- public enum PhotoLibraryAccessLevel
- {
- /// <summary>Can only add photos to the photo library, cannot create albums or read back images.</summary>
- AddOnly,
- /// <summary>Full access, can add photos, create albums and read back images.</summary>
- ReadWrite
- };
- /// <summary>
- /// Authorisation for access to the photos library.
- /// </summary>
- public enum PhotoLibraryAuthorisationStatus
- {
- /// <summary>The photo library is unavailable.</summary>
- Unavailable = -1,
- /// <summary>Authorisation to the photo library is still to be requested.</summary>
- NotDetermined,
- /// <summary>Access to the photo library has been denied.</summary>
- Denied,
- /// <summary>Access to the photo library has been granted.</summary>
- Authorised
- };
- /// <summary>
- /// Check to see if authorisation has been given to access the photo library.
- /// </summary>
- private static bool _waitingForAuthorisationToAccessPhotos = true;
- private static bool _hasCheckedPhotoLibraryAuthorisationStatus = false;
- private static PhotoLibraryAuthorisationStatus _photoLibraryAuthorisation = PhotoLibraryAuthorisationStatus.NotDetermined;
- public static PhotoLibraryAuthorisationStatus HasUserAuthorisationToAccessPhotos(PhotoLibraryAccessLevel accessLevel)
- {
- if (!_hasCheckedPhotoLibraryAuthorisationStatus || _photoLibraryAuthorisation == PhotoLibraryAuthorisationStatus.NotDetermined)
- {
- _hasCheckedPhotoLibraryAuthorisationStatus = true;
- #if !UNITY_EDITOR_OSX && (UNITY_STANDALONE_OSX || (UNITY_IOS && !UNITY_EDITOR))
- _photoLibraryAuthorisation = (PhotoLibraryAuthorisationStatus)NativePlugin.PhotoLibraryAuthorisationStatus((int)accessLevel);
- #else
- _photoLibraryAuthorisation = PhotoLibraryAuthorisationStatus.Unavailable;
- #endif
- }
- return _photoLibraryAuthorisation;
- }
- #if ENABLE_IL2CPP && (UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX || (UNITY_IOS && !UNITY_EDITOR))
- [MonoPInvokeCallback(typeof(NativePlugin.RequestPhotoLibraryAuthorisationDelegate))]
- #endif
- private static void RequestUserAuthorisationToAccessPhotosCallback(int authorisation)
- {
- _photoLibraryAuthorisation = (PhotoLibraryAuthorisationStatus)authorisation;
- _hasCheckedPhotoLibraryAuthorisationStatus = true;
- _waitingForAuthorisationToAccessPhotos = false;
- }
- private class WaitForAuthorisationToAccessPhotos: CustomYieldInstruction
- {
- public override bool keepWaiting { get { return CaptureBase._waitingForAuthorisationToAccessPhotos; } }
- }
- /// <summary>
- /// Request authorisation to access the photo library.
- /// </summary>
- public static CustomYieldInstruction RequestUserAuthorisationToAccessPhotos(PhotoLibraryAccessLevel accessLevel)
- {
- #if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX || (UNITY_IOS && !UNITY_EDITOR)
- NativePlugin.RequestPhotoLibraryAuthorisationDelegate callback = new NativePlugin.RequestPhotoLibraryAuthorisationDelegate(RequestUserAuthorisationToAccessPhotosCallback);
- System.IntPtr ptr = Marshal.GetFunctionPointerForDelegate(callback);
- NativePlugin.RequestPhotoLibraryAuthorisation((int)accessLevel, ptr);
- return new WaitForAuthorisationToAccessPhotos();
- #else
- return null;
- #endif
- }
- }
- }
|