Ffcc.cs 57 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611
  1. namespace OpenVIII.AV
  2. {
  3. using FFmpeg.AutoGen;
  4. using Microsoft.Xna.Framework.Audio;
  5. using Microsoft.Xna.Framework.Graphics;
  6. using NAudio.Wave;
  7. using NAudio.Wave.SampleProviders;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Diagnostics;
  11. using System.IO;
  12. using System.Linq;
  13. using System.Runtime.InteropServices;
  14. using System.Threading;
  15. using System.Threading.Tasks;
  16. /// <summary>
  17. /// Ffcc is a front end for processing Audio and Video using ffmpeg.autogen
  18. /// </summary>
  19. /// <remarks>
  20. /// Code bits mostly converted to c# from c++ It uses bits of code from FFmpeg examples, Aforge,
  21. /// FFmpeg.autogen, stackoverflow
  22. /// </remarks>
  23. public abstract partial class Ffcc : IDisposable
  24. {
  25. /// <summary>
  26. /// Opens filename and init class.
  27. /// </summary>
  28. protected static T Load<T>(string filename, AVMediaType mediatype = AVMediaType.AVMEDIA_TYPE_AUDIO, FfccMode mode = FfccMode.STATE_MACH, int loopstart = -1) where T : Ffcc, new()
  29. {
  30. T r = new T();
  31. r.Init(filename, mediatype, mode, loopstart);
  32. if (mode == FfccMode.PROCESS_ALL)
  33. r.Dispose(false);
  34. return r;
  35. }
  36. #region Fields
  37. /// <summary>
  38. /// If you have sound skipping increase this number and it'll go away. might decrease sync or
  39. /// increase memory load The goal is to keep the dynamicsoundeffectinterface fed. If it plays
  40. /// the audio before you give it more, then you get sound skips.
  41. /// </summary>
  42. /// <value>The goal buffer count.</value>
  43. /// <remarks>
  44. /// Will want to be as low as possible without sound skipping. 91.875 is 1 second of audio at
  45. /// 44100 hz @ 15 fps; 99.9001 is 1 second of audio at 48000 hz @ 15 fps;
  46. /// </remarks>
  47. private const int GoalBufferCount = 100;
  48. /// <summary>
  49. /// NextAsync sleeps when filling buffer. If set too high buffer will empty before filling it again.
  50. /// </summary>
  51. private const int NextAsyncSleep = 10;
  52. /// <summary>
  53. /// on exception this is turned to true and will force fallback to naudio when monogame isn't working.
  54. /// </summary>
  55. private static bool useNaudio = false;
  56. private object DynamicSoundEffectLock = new object();
  57. private readonly unsafe AVDictionary* _dict;
  58. private unsafe AVIOContext* _avio_ctx;
  59. private unsafe byte* _avio_ctx_buffer;
  60. private int _avio_ctx_buffer_size;
  61. private byte[] _convertedData;
  62. //private byte* _convertedData;
  63. private MemoryStream _decodedMemoryStream;
  64. private bool _frameSkip = true;
  65. //private IntPtr _intPtr;
  66. private int _loopstart;
  67. /// <summary>
  68. /// buffered wave provider used to handle audio samples
  69. /// </summary>
  70. /// <see cref="https://markheath.net/post/how-to-record-and-play-audio-at-same"/>
  71. private CancellationToken cancellationToken;
  72. #if _WINDOWS
  73. private BufferedWaveProvider bufferedWaveProvider;
  74. /// <summary>
  75. /// Wave out for naudio only works in windows.
  76. /// </summary>
  77. /// <see cref="https://markheath.net/post/how-to-record-and-play-audio-at-same"/>
  78. private DirectSoundOut nAudioOut;
  79. /// <summary>
  80. /// Directsound requires VolumeSampleProvider to change volume.
  81. /// </summary>
  82. private VolumeSampleProvider volumeSampleProvider;
  83. /// <summary>
  84. /// Required by naudio to pan the sound.
  85. /// </summary>
  86. private PanningSampleProvider panningSampleProvider;
  87. #endif
  88. private avio_alloc_context_read_packet rf;
  89. private CancellationTokenSource sourceToken;
  90. private bool stopped = false;
  91. private Task task;
  92. private FfccVaribleGroup _decoder = new FfccVaribleGroup();
  93. private DynamicSoundEffectInstance _dynamicSound;
  94. private int _loopend;
  95. private int _looplength;
  96. #endregion Fields
  97. #region Destructors
  98. ~Ffcc()
  99. {
  100. Dispose(false);
  101. }
  102. #endregion Destructors
  103. #region Enums
  104. //public FileStream DecodeFileStream { get => _decodeFileStream; set => _decodeFileStream = value; }
  105. public enum FfccMode
  106. {
  107. /// <summary>
  108. /// Processes entire file at once and does something with output
  109. /// </summary>
  110. PROCESS_ALL,
  111. /// <summary>
  112. /// State machine, functions in this call update to update current state. And update
  113. /// decides what to do next.
  114. /// </summary>
  115. STATE_MACH,
  116. /// <summary>
  117. /// Not Init some error happened that prevented the class from working
  118. /// </summary>
  119. NOTINIT
  120. }
  121. public enum FfccState
  122. {
  123. OPEN,
  124. /// <summary>
  125. /// Waiting for request for next frame
  126. /// </summary>
  127. WAITING,
  128. /// <summary>
  129. /// Readall the data
  130. /// </summary>
  131. READALL,
  132. /// <summary>
  133. /// Done reading file nothing more can be done
  134. /// </summary>
  135. DONE,
  136. /// <summary>
  137. /// Don't change state just pass ret value.
  138. /// </summary>
  139. NULL,
  140. /// <summary>
  141. /// Get packet of data containing frames
  142. /// </summary>
  143. READONE,
  144. /// <summary>
  145. /// Missing DLL required to function
  146. /// </summary>
  147. NODLL,
  148. /// <summary>
  149. /// Gets stream and Codec
  150. /// </summary>
  151. GETCODEC,
  152. /// <summary>
  153. /// Prepares Scaler for Video stream
  154. /// </summary>
  155. PREPARE_SWS,
  156. /// <summary>
  157. /// Start Reading
  158. /// </summary>
  159. READ,
  160. /// <summary>
  161. /// Prepares Resampler for Audio stream
  162. /// </summary>
  163. PREPARE_SWR
  164. }
  165. #endregion Enums
  166. #region Properties
  167. /// <summary>
  168. /// Are you ahead of target frame
  169. /// </summary>
  170. /// <returns>true if ahead</returns>
  171. public bool Ahead
  172. {
  173. get
  174. {
  175. if (Decoder.StreamIndex != -1 && Mode == FfccMode.STATE_MACH)
  176. {
  177. if (MediaType == AVMediaType.AVMEDIA_TYPE_AUDIO)
  178. {
  179. if (DynamicSound != null)
  180. {
  181. return DynamicSound.PendingBufferCount > GoalBufferCount;
  182. }
  183. #if _WINDOWS
  184. else if (useNaudio)
  185. {
  186. return bufferedWaveProvider.BufferedDuration.TotalSeconds > bufferedWaveProvider.BufferDuration.TotalSeconds - 1;
  187. }
  188. #endif
  189. }
  190. else if (timer.IsRunning)
  191. {
  192. return CurrentFrameNum > ExpectedFrame;
  193. }
  194. }
  195. return true;
  196. }
  197. }
  198. /// <summary>
  199. /// Are you behind target frame
  200. /// </summary>
  201. /// <returns>true if behind</returns>
  202. public bool Behind => !Ahead && !Current;
  203. /// <summary>
  204. /// Are you on target frame
  205. /// </summary>
  206. /// <returns>true if correct frame</returns>
  207. public unsafe bool Current
  208. {
  209. get
  210. {
  211. if (Decoder.StreamIndex != -1 && Mode == FfccMode.STATE_MACH)
  212. {
  213. if (MediaType == AVMediaType.AVMEDIA_TYPE_AUDIO)
  214. {
  215. if (DynamicSound != null)
  216. {
  217. return DynamicSound.PendingBufferCount == GoalBufferCount;
  218. }
  219. #if _WINDOWS
  220. else if (useNaudio)
  221. {
  222. return bufferedWaveProvider.BufferedDuration.TotalSeconds == bufferedWaveProvider.BufferDuration.TotalSeconds - 1;
  223. }
  224. #endif
  225. else
  226. {
  227. die($"{Decoder.CodecContext->sample_rate} is currently unsupported");
  228. }
  229. }
  230. else if (timer.IsRunning)
  231. {
  232. return CurrentFrameNum == ExpectedFrame;
  233. }
  234. }
  235. return false;
  236. }
  237. }
  238. /// <summary>
  239. /// Path and filename of file.
  240. /// </summary>
  241. public string DecodedFileName { get; private set; }
  242. /// <summary>
  243. /// Dynamic Sound Effect Interface for class allows control out of class. Mode must be in STATE_MACH
  244. /// </summary>
  245. public DynamicSoundEffectInstance DynamicSound
  246. {
  247. get => _dynamicSound; private set
  248. {
  249. lock (DynamicSoundEffectLock)
  250. _dynamicSound = value;
  251. }
  252. }
  253. /// <summary>
  254. /// True if file is open.
  255. /// </summary>
  256. public bool FileOpened { get; private set; }
  257. /// <summary>
  258. /// returns Frames per second or if that is 0. it will return the Time_Base ratio. This is
  259. /// the fundamental unit of time (in seconds) in terms of which frame timestamps are
  260. /// represented. In many cases the audio files time base is the same as the sample rate.
  261. /// example 1/44100. video files audio stream might be 1/100 or 1/1000. these can make for
  262. /// large durrations.
  263. /// </summary>
  264. public unsafe double FPS
  265. {
  266. get
  267. {
  268. double r = FPSvideo;
  269. if (r != 0)
  270. {
  271. return r;
  272. }
  273. else
  274. {
  275. if (MediaType == AVMediaType.AVMEDIA_TYPE_AUDIO && Decoder.CodecContext != null && Decoder.CodecContext->framerate.den != 0)
  276. {
  277. return Decoder.CodecContext->framerate.num / (double)Decoder.CodecContext->framerate.den;
  278. }
  279. else if (Decoder.Stream != null && Decoder.Stream->time_base.den != 0)
  280. {
  281. return Decoder.Stream->time_base.num / (double)Decoder.Stream->time_base.den; // returns the time_base
  282. }
  283. }
  284. return 0;
  285. }
  286. }
  287. /// <summary>
  288. /// When getting video frames if behind it goes to next frame. disabled for debugging purposes.
  289. /// </summary>
  290. public bool FrameSkip { get => MediaType == AVMediaType.AVMEDIA_TYPE_VIDEO ? _frameSkip : false; set => _frameSkip = value; }
  291. /// <summary>
  292. /// Is the class disposed of. If true calling Dispose() does nothing.
  293. /// </summary>
  294. public bool IsDisposed { get; private set; } = false;
  295. /// <summary>
  296. /// Sample count that loop starts from.
  297. /// </summary>
  298. public int LOOPSTART { get => _loopstart; set => _loopstart = value; }
  299. /// <summary>
  300. /// Current media type being processed.
  301. /// </summary>
  302. public AVMediaType MediaType { get; private set; }
  303. /// <summary>
  304. /// Metadata container for tags.
  305. /// </summary>
  306. public Dictionary<String, String> Metadata { get; private set; } = new Dictionary<string, string>();
  307. /// <summary>
  308. /// SoundEffect for class allows control out of class. Mode must be in PROCESS_ALL
  309. /// </summary>
  310. public SoundEffect SoundEffect { get; private set; }
  311. /// <summary>
  312. /// SoundEffectInterface for class. allows for more control than just playing the above soundeffect.
  313. /// </summary>
  314. public SoundEffectInstance SoundEffectInstance { get; private set; }
  315. /// <summary>
  316. /// Stopwatch tracks the time audio has played so video can sync or loops can be looped.
  317. /// </summary>
  318. public Stopwatch timer { get; } = new Stopwatch();
  319. /// <summary>
  320. /// if there is no stream it returns false. only checked when trying to process audio
  321. /// </summary>
  322. private bool AudioEnabled => Decoder.StreamIndex >= 0;
  323. //private byte* ConvertedData { get => _convertedData; set => _convertedData = value; }
  324. private byte[] ConvertedData { get => _convertedData; set => _convertedData = value; }
  325. /// <summary>
  326. /// Current frame number
  327. /// </summary>
  328. /// <returns>Current frame number or -1 on error</returns>
  329. public unsafe int CurrentFrameNum => Decoder.CodecContext != null ? Decoder.CodecContext->frame_number : -1;
  330. /// <summary>
  331. /// MemoryStream of Audio after decoding and resamping to compatable format.
  332. /// </summary>
  333. private MemoryStream DecodedMemoryStream { get => _decodedMemoryStream; set => _decodedMemoryStream = value; }
  334. /// <summary>
  335. /// Holder of varibles for Decoder
  336. /// </summary>
  337. protected FfccVaribleGroup Decoder { get => _decoder; set => _decoder = value; }
  338. /// <summary>
  339. /// Based on timer and FPS determines what the current frame is.
  340. /// </summary>
  341. /// <returns>Expected Frame Number</returns>
  342. private int ExpectedFrame => timer.IsRunning ? (int)Math.Round(timer.ElapsedMilliseconds * (FPS / 1000)) : 0;
  343. /// <summary>
  344. /// FPS of the video stream.
  345. /// </summary>
  346. private unsafe double FPSvideo
  347. {
  348. get
  349. {
  350. Return = ffmpeg.av_find_best_stream(Decoder.Format, AVMediaType.AVMEDIA_TYPE_VIDEO, -1, -1, null, 0);
  351. if (Return < 0)
  352. {
  353. return 0;
  354. }
  355. else if (Decoder.Format->streams[Return]->codec->framerate.den > 0)
  356. {
  357. return (double)Decoder.Format->streams[Return]->codec->framerate.num / Decoder.Format->streams[Return]->codec->framerate.den;
  358. }
  359. return 0;
  360. }
  361. }
  362. /// <summary>
  363. /// Mode that ffcc is running in.
  364. /// </summary>
  365. private FfccMode Mode { get; set; }
  366. /// <summary>
  367. /// Resample Context
  368. /// </summary>
  369. private unsafe SwrContext* ResampleContext { get; set; }
  370. /// <summary>
  371. /// Frame used by resampler
  372. /// </summary>
  373. private unsafe AVFrame* ResampleFrame { get; set; }
  374. /// <summary>
  375. /// Most ffmpeg functions return an integer. If the value is less than 0 it is an error
  376. /// usually. Sometimes data is passed and then it will be greater than 0.
  377. /// </summary>
  378. private int Return { get; set; }
  379. /// <summary>
  380. /// SWS Context
  381. /// </summary>
  382. private unsafe SwsContext* ScalerContext { get; set; }
  383. /// <summary>
  384. /// State ffcc is in.
  385. /// </summary>
  386. private FfccState State { get; set; }
  387. #endregion Properties
  388. #region Methods
  389. /// <summary>
  390. /// Dispose of all leaky varibles.
  391. /// </summary>
  392. public void Dispose() => Dispose(true);
  393. /// <summary>
  394. /// Attempts to get 1 frame of Video, or refill Audio buffer.
  395. /// </summary>
  396. /// <returns>Returns -1 if missing stream or returns AVERROR or returns 0 if no problem.</returns>
  397. public int Next()
  398. {
  399. //if stream doesn't exist or stream is done, end
  400. if (Decoder.StreamIndex == -1 || State == FfccState.DONE)
  401. {
  402. return -1;
  403. }
  404. // read next frame(s)
  405. else
  406. {
  407. return Update(FfccState.READONE);
  408. }
  409. }
  410. /// <summary>
  411. /// Pause or Resume timer. WIP
  412. /// </summary>
  413. public void Pause()
  414. {
  415. if (Decoder.StreamIndex > -1)
  416. {
  417. if (Mode == FfccMode.STATE_MACH)
  418. {
  419. if (!timer.IsRunning)
  420. {
  421. timer.Stop();
  422. }
  423. else
  424. {
  425. timer.Start();
  426. }
  427. }
  428. }
  429. }
  430. /// <summary>
  431. /// Start playing Sound or Start FPS timer for Video
  432. /// </summary>
  433. /// <param name="volume">
  434. /// Volume, ranging from 0.0 (silence) to 1.0 (full volume). Volume during playback is scaled
  435. /// by SoundEffect.MasterVolume.
  436. /// </param>
  437. /// <param name="pitch">
  438. /// Pitch adjustment, ranging from -1.0 (down an octave) to 0.0 (no change) to 1.0 (up an octave).
  439. /// </param>
  440. /// <param name="pan">
  441. /// Panning, ranging from -1.0 (left speaker) to 0.0 (centered), 1.0 (right speaker).
  442. /// </param>
  443. public void Play(float volume = 1.0f, float pitch = 0.0f, float pan = 0.0f) // there are some videos without sound meh.
  444. {
  445. if (Decoder != null && Decoder.StreamIndex > -1)
  446. {
  447. if (!timer.IsRunning && Mode == FfccMode.STATE_MACH && MediaType == AVMediaType.AVMEDIA_TYPE_VIDEO)
  448. {
  449. timer.Start();
  450. }
  451. if (!useNaudio)
  452. {
  453. if (DynamicSound != null && !DynamicSound.IsDisposed && AudioEnabled)
  454. {
  455. lock (DynamicSoundEffectLock)
  456. {
  457. DynamicSound.Volume = volume;
  458. DynamicSound.Pitch = pitch;
  459. DynamicSound.Pan = pan;
  460. DynamicSound.Play();
  461. }
  462. }
  463. if (SoundEffect != null && !SoundEffect.IsDisposed && AudioEnabled)
  464. {
  465. SoundEffectInstance.Volume = volume;
  466. SoundEffectInstance.Pitch = pitch;
  467. SoundEffectInstance.Pan = pan;
  468. try
  469. {
  470. SoundEffectInstance.Play();
  471. }
  472. catch (Exception e)
  473. {
  474. if (e.GetType().Name == "SharpDXException")
  475. {
  476. Mode = FfccMode.NOTINIT;
  477. State = FfccState.NODLL;
  478. SoundEffectInstance = null;
  479. SoundEffect = null;
  480. // if it gets here I can't extract the sound from the SoundEffect but
  481. // I can turn on nAudio and next sound will work
  482. useNaudio = true;
  483. }
  484. else
  485. e.Rethrow();
  486. }
  487. }
  488. }
  489. #if _WINDOWS
  490. else if (bufferedWaveProvider != null && nAudioOut != null)
  491. {
  492. volumeSampleProvider.Volume = volume;
  493. if (panningSampleProvider != null) // panning requires mono sound so it's null if it wasn't 1 channel.
  494. panningSampleProvider.Pan = pan;
  495. // i'll leave out pitch unless it's needed. there is a provider for it but sounds
  496. // like it might do more than we need.
  497. nAudioOut.Play();
  498. }
  499. #endif
  500. }
  501. }
  502. /// <summary>
  503. /// Same as Play but with a task. Thread is terminated on Stop() or Dispose().
  504. /// </summary>
  505. public void PlayInTask(float volume = 1.0f, float pitch = 0.0f, float pan = 0.0f)
  506. {
  507. if (MediaType == AVMediaType.AVMEDIA_TYPE_AUDIO)
  508. {
  509. if (sourceToken == null)
  510. sourceToken = new CancellationTokenSource();
  511. if (cancellationToken == null)
  512. cancellationToken = sourceToken.Token;
  513. Play(volume, pitch, pan);
  514. task = new Task<int>(NextinTask);
  515. task.Start();
  516. }
  517. else
  518. Play(volume, pitch, pan);
  519. }
  520. /// <summary>
  521. /// Stop playing Sound or Stop the FPS timer for Video , and Dispose of Varibles
  522. /// </summary>
  523. public async void Stop()
  524. {
  525. if (stopped)
  526. return;
  527. if (timer.IsRunning)
  528. {
  529. timer.Stop();
  530. timer.Reset();
  531. }
  532. if (!useNaudio)
  533. {
  534. if (DynamicSound != null && !DynamicSound.IsDisposed)
  535. {
  536. lock (DynamicSoundEffectLock)
  537. {
  538. if (AudioEnabled)
  539. {
  540. try
  541. {
  542. DynamicSound?.Stop();
  543. }
  544. catch (NullReferenceException)
  545. {
  546. }
  547. }
  548. DynamicSound.Dispose();
  549. }
  550. DynamicSound = null;
  551. }
  552. if (SoundEffectInstance != null && !SoundEffectInstance.IsDisposed)
  553. {
  554. if (AudioEnabled)
  555. {
  556. SoundEffectInstance.Stop();
  557. }
  558. SoundEffectInstance.Dispose();
  559. }
  560. if (SoundEffect != null && !SoundEffect.IsDisposed)
  561. {
  562. SoundEffect.Dispose();
  563. }
  564. }
  565. #if _WINDOWS
  566. else if (bufferedWaveProvider != null && nAudioOut != null)
  567. {
  568. nAudioOut.Stop();
  569. try
  570. {
  571. nAudioOut.Dispose();
  572. bufferedWaveProvider.ClearBuffer();
  573. }
  574. catch (InvalidOperationException)
  575. {
  576. // naudio can't be disposed of if not in original thread.
  577. }
  578. }
  579. #endif
  580. if (task != null)
  581. {
  582. sourceToken.Cancel();
  583. await task;
  584. }
  585. }
  586. /// <summary>
  587. /// Converts Frame to Texture with correct colorspace
  588. /// </summary>
  589. /// <returns>Texture2D</returns>
  590. public unsafe Texture2D Texture2D()
  591. {
  592. lock (Decoder)
  593. {
  594. Texture2D frameTex = new Texture2D(Memory.spriteBatch.GraphicsDevice, Decoder.CodecContext->width, Decoder.CodecContext->height, false, SurfaceFormat.Color);
  595. const int bpp = 4;
  596. byte[] texBuffer = new byte[Decoder.CodecContext->width * Decoder.CodecContext->height * bpp];
  597. fixed (byte* ptr = &texBuffer[0])
  598. {
  599. byte*[] srcData = { ptr, null, null, null };
  600. int[] srcLinesize = { Decoder.CodecContext->width * bpp, 0, 0, 0 };
  601. // convert video frame to the RGB data
  602. ffmpeg.sws_scale(ScalerContext, Decoder.Frame->data, Decoder.Frame->linesize, 0, Decoder.CodecContext->height, srcData, srcLinesize);
  603. }
  604. frameTex.SetData(texBuffer);
  605. return frameTex;
  606. }
  607. }
  608. protected virtual void Dispose(bool disposing)
  609. {
  610. if (Decoder != null)
  611. {
  612. lock (Decoder)
  613. { dis(); }
  614. }
  615. else dis();
  616. void dis()
  617. {
  618. if (disposing)
  619. {
  620. Stop();
  621. }
  622. if (!IsDisposed)
  623. {
  624. if (task != null)
  625. {
  626. if (task.IsCompleted)
  627. task.Dispose();
  628. else
  629. Memory.LeftOverTask.Add(task);
  630. task = null;
  631. }
  632. State = FfccState.DONE;
  633. Mode = FfccMode.NOTINIT;
  634. // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
  635. // TODO: set large fields to null.
  636. if (DecodedMemoryStream != null)
  637. {
  638. DecodedMemoryStream.Dispose();
  639. }
  640. if (ConvertedData != null)
  641. {
  642. //Marshal.FreeHGlobal((IntPtr)ConvertedData);
  643. }
  644. //if (_intPtr != null)
  645. //{
  646. // Marshal.FreeHGlobal(_intPtr);
  647. //}
  648. unsafe
  649. {
  650. ffmpeg.sws_freeContext(ScalerContext);
  651. if (ResampleContext != null)
  652. {
  653. ffmpeg.swr_close(ResampleContext);
  654. SwrContext* pResampleContext = ResampleContext;
  655. ffmpeg.swr_free(&pResampleContext);
  656. }
  657. ffmpeg.av_frame_unref(ResampleFrame);
  658. ffmpeg.av_free(ResampleFrame);
  659. if (_avio_ctx != null)
  660. {
  661. //ffmpeg.avio_close(avio_ctx); //CTD
  662. ffmpeg.av_free(_avio_ctx);
  663. }
  664. }
  665. //if (avio_ctx_buffer != null)
  666. // ffmpeg.av_freep(avio_ctx_buffer); //throws exception
  667. // set to true to prevent multiple disposings
  668. IsDisposed = true;
  669. //GC.Collect(); // donno if this really does much. was trying to make sure the memory i'm watching is what is really there.
  670. }
  671. else
  672. {
  673. if (nAudioOut != null && useNaudio)
  674. {
  675. nAudioOut.Dispose();
  676. nAudioOut = null;
  677. bufferedWaveProvider.ClearBuffer();
  678. }
  679. if (sourceToken != null)
  680. {
  681. sourceToken.Dispose();
  682. sourceToken = null;
  683. }
  684. if (_decoder != null)
  685. {
  686. _decoder.Dispose();
  687. _decoder = null;
  688. }
  689. }
  690. }
  691. }
  692. /// <summary>
  693. /// Flush the Decoder context and packet
  694. /// </summary>
  695. /// <param name="avctx">Decoder Codec Context</param>
  696. /// <param name="avpkt">Decoder Packet</param>
  697. /// <returns>0 on success, less than 0 on error</returns>
  698. private static unsafe int DecodeFlush(ref AVCodecContext* avctx, ref AVPacket avpkt)
  699. {
  700. avpkt.data = null;
  701. avpkt.size = 0;
  702. fixed (AVPacket* tmpPacket = &avpkt)
  703. {
  704. return ffmpeg.avcodec_send_packet(avctx, tmpPacket);
  705. }
  706. }
  707. /// <summary>
  708. /// throw new exception
  709. /// </summary>
  710. /// <param name="v">string of message</param>
  711. private static void die(string v) => throw new Exception(v.Trim('\0'));
  712. /// <summary>
  713. /// For reading data from a memory pointer as if it's a file.
  714. /// </summary>
  715. /// <param name="opaque">incoming data</param>
  716. /// <param name="buf">outgoing data</param>
  717. /// <param name="buf_size">outgoing buffer size</param>
  718. /// <returns>Total bytes read, or EOF</returns>
  719. private static unsafe int Read_packet(void* opaque, byte* buf, int buf_size)
  720. {
  721. BufferData* bd = (BufferData*)opaque;
  722. return bd->Read(buf, buf_size);
  723. }
  724. /// <summary>
  725. /// Converts FFMPEG error codes into a string.
  726. /// </summary>
  727. private unsafe string AvError(int ret)
  728. {
  729. ulong errbuff_size = 256;
  730. byte[] errbuff = new byte[errbuff_size];
  731. fixed (byte* ptr = &errbuff[0])
  732. {
  733. ffmpeg.av_strerror(ret, ptr, errbuff_size);
  734. }
  735. return System.Text.Encoding.UTF8.GetString(errbuff).Trim('\0');
  736. }
  737. /// <summary>
  738. /// Throws exception if Ret is less than 0
  739. /// </summary>
  740. private int CheckReturn()
  741. {
  742. switch (Return)
  743. {
  744. case ffmpeg.AVERROR_OUTPUT_CHANGED:
  745. die($"The swr_context output ch_layout, sample_rate, sample_fmt must match outframe! {Return} - {AvError(Return)}");
  746. break;
  747. case ffmpeg.AVERROR_INPUT_CHANGED:
  748. die($"The swr_context input ch_layout, sample_rate, sample_fmt must match inframe! {Return} - {AvError(Return)}");
  749. break;
  750. default:
  751. if (Return < 0)
  752. {
  753. die($"{Return} - {AvError(Return)}");
  754. }
  755. break;
  756. }
  757. return Return;
  758. }
  759. /// <summary>
  760. /// Decode the next frame.
  761. /// </summary>
  762. /// <param name="frame">Current Decoded Frame</param>
  763. /// <returns>false if EOF, or true if grabbed frame</returns>
  764. private unsafe bool Decode(out AVFrame frame)
  765. {
  766. do
  767. {
  768. //need this extra receive frame for when decoding audio with >1 frame per packet
  769. Return = ffmpeg.avcodec_receive_frame(Decoder.CodecContext, Decoder.Frame);
  770. if (Return == ffmpeg.AVERROR(ffmpeg.EAGAIN))
  771. {
  772. do
  773. {
  774. do
  775. {
  776. //make sure packet is unref before getting a new one.
  777. ffmpeg.av_packet_unref(Decoder.Packet);
  778. Return = ffmpeg.av_read_frame(Decoder.Format, Decoder.Packet);
  779. if (Return == ffmpeg.AVERROR_EOF)
  780. {
  781. goto EOF;
  782. }
  783. else
  784. {
  785. CheckReturn();
  786. }
  787. }
  788. //check for correct stream.
  789. while (Decoder.Packet->stream_index != Decoder.StreamIndex);
  790. Return = ffmpeg.avcodec_send_packet(Decoder.CodecContext, Decoder.Packet);
  791. ffmpeg.av_packet_unref(Decoder.Packet);
  792. CheckReturn();
  793. Return = ffmpeg.avcodec_receive_frame(Decoder.CodecContext, Decoder.Frame);
  794. }
  795. while (Return == ffmpeg.AVERROR(ffmpeg.EAGAIN));
  796. CheckReturn();
  797. }
  798. else if (Return == ffmpeg.AVERROR_EOF)
  799. {
  800. goto EOF;
  801. }
  802. else
  803. {
  804. CheckReturn();
  805. }
  806. }
  807. //check for frameskip, if enabled check if behind.
  808. while (FrameSkip && Behind);
  809. frame = *Decoder.Frame;
  810. return true;
  811. //end of file, check for loop and end.
  812. //TODO: add LOOPEND and LOOPLENGTH support https://github.com/FFT-Hackers/vgmstream/commit/b332e5cf5cc9e3469cbbab226836083d8377ce61
  813. EOF:
  814. Loop();
  815. frame = *Decoder.Frame;
  816. return false;
  817. }
  818. /// <summary>
  819. /// reads the tags from metadata
  820. /// </summary>
  821. /// <param name="metadata">metadata from format or stream</param>
  822. private unsafe void GetTags(ref AVDictionary* metadata)
  823. {
  824. AVDictionaryEntry* tag = null;
  825. while ((tag = ffmpeg.av_dict_get(metadata, "", tag, ffmpeg.AV_DICT_IGNORE_SUFFIX)) != null)
  826. {
  827. string key = "";
  828. string val = "";
  829. for (int i = 0; tag->value[i] != 0; i++)
  830. {
  831. val += (char)tag->value[i];
  832. }
  833. for (int i = 0; tag->key[i] != 0; i++)
  834. {
  835. key += (char)tag->key[i];
  836. }
  837. Metadata[key.Trim().ToUpper()] = val;
  838. Debug.WriteLine($"{key} = {val}");
  839. if (key.Trim().IndexOf("LOOPSTART", StringComparison.OrdinalIgnoreCase) >= 0)
  840. {
  841. if (!int.TryParse(val, out _loopstart))
  842. {
  843. _loopstart = 0;
  844. Debug.WriteLine($"Failed Parse {key} = {val}");
  845. }
  846. }
  847. else if (key.Trim().IndexOf("LOOPEND", StringComparison.OrdinalIgnoreCase) >= 0)
  848. {
  849. if (int.TryParse(val, out _loopend))
  850. _looplength = _loopend - _loopstart;
  851. else
  852. Debug.WriteLine($"Failed Parse {key} = {val}");
  853. }
  854. else if (key.Trim().IndexOf("LOOPLENGTH", StringComparison.OrdinalIgnoreCase) >= 0)
  855. {
  856. if (int.TryParse(val, out _looplength))
  857. _loopend = _loopend + _loopstart;
  858. else
  859. Debug.WriteLine($"Failed Parse {key} = {val}");
  860. }
  861. }
  862. }
  863. /// <summary>
  864. /// Opens filename and init class.
  865. /// </summary>
  866. protected void Init(string filename, AVMediaType mediatype = AVMediaType.AVMEDIA_TYPE_AUDIO, FfccMode mode = FfccMode.STATE_MACH, int loopstart = -1)
  867. {
  868. ffmpeg.av_log_set_level(ffmpeg.AV_LOG_PANIC);
  869. LOOPSTART = loopstart;
  870. State = FfccState.OPEN;
  871. Mode = mode;
  872. DecodedFileName = filename;
  873. MediaType = mediatype;
  874. Return = -1;
  875. ConvertedData = null;
  876. Update();
  877. }
  878. private unsafe bool initNaudio()
  879. {
  880. #if _WINDOWS
  881. if (!Extended.IsLinux)
  882. {
  883. bufferedWaveProvider = new BufferedWaveProvider(
  884. new WaveFormat(ResampleFrame->sample_rate, ResampleFrame->channels))
  885. {
  886. DiscardOnBufferOverflow = false
  887. };
  888. volumeSampleProvider = new VolumeSampleProvider(bufferedWaveProvider.ToSampleProvider());
  889. nAudioOut = new DirectSoundOut();
  890. if (ResampleFrame->channels == 1)
  891. {
  892. panningSampleProvider = new PanningSampleProvider(volumeSampleProvider);
  893. nAudioOut.Init(panningSampleProvider);
  894. }
  895. else
  896. nAudioOut.Init(volumeSampleProvider);
  897. useNaudio = true;
  898. return true;
  899. }
  900. #endif
  901. return false;
  902. }
  903. /// <summary>
  904. /// Sets up AVFormatContext to be able from the memory buffer.
  905. /// </summary>
  906. protected unsafe void LoadFromRAM(BufferData* bd)
  907. {
  908. _avio_ctx = null;
  909. _avio_ctx_buffer_size = 4096;
  910. int ret = 0;
  911. //_bufferData = new Buffer_Data
  912. //{
  913. // Header = buffer,
  914. // HeaderSize = buffer_size
  915. //};
  916. _avio_ctx_buffer = (byte*)ffmpeg.av_malloc((ulong)_avio_ctx_buffer_size);
  917. if (_avio_ctx_buffer == null)
  918. {
  919. ret = ffmpeg.AVERROR(ffmpeg.ENOMEM);
  920. return;
  921. }
  922. rf = new avio_alloc_context_read_packet(Read_packet);
  923. _avio_ctx = ffmpeg.avio_alloc_context(_avio_ctx_buffer, _avio_ctx_buffer_size, 0, bd, rf, null, null);
  924. if (_avio_ctx == null)
  925. {
  926. ret = ffmpeg.AVERROR(ffmpeg.ENOMEM);
  927. }
  928. Decoder._format->pb = _avio_ctx;
  929. Open();
  930. }
  931. /// <summary>
  932. /// Load sound from Memorystream into a SoundEFFect
  933. /// </summary>
  934. /// <param name="decodedStream">Memory Stream containing sound data</param>
  935. private unsafe void LoadSoundFromStream(ref MemoryStream decodedStream)
  936. {
  937. if (DecodedMemoryStream.Length > 0 && MediaType == AVMediaType.AVMEDIA_TYPE_AUDIO)
  938. {
  939. if (!useNaudio)
  940. {
  941. if (SoundEffect == null)
  942. {
  943. SoundEffect = new SoundEffect(decodedStream.GetBuffer(), 0, (int)decodedStream.Length, ResampleFrame->sample_rate, (AudioChannels)ResampleFrame->channels, 0, 0);
  944. SoundEffectInstance = SoundEffect.CreateInstance();
  945. if (LOOPSTART >= 0)
  946. {
  947. SoundEffectInstance.IsLooped = true;
  948. }
  949. //doesn't throw an exception till you goto play it.
  950. }
  951. }
  952. else
  953. RecorderOnDataAvailable(this, new WaveInEventArgs(decodedStream.GetBuffer(), (int)decodedStream.Length));
  954. }
  955. }
  956. /// <summary>
  957. /// Copies byte[] data to a Pointer. So it can be used with ffmpeg.
  958. /// </summary>
  959. /// <param name="data">incoming data</param>
  960. /// <param name="length">size of data</param>
  961. //private void LoadFromRAM(byte[] data, int length)
  962. //{
  963. // _intPtr = Marshal.AllocHGlobal(length);
  964. // Marshal.Copy(data, 0, _intPtr, length);
  965. // LoadFromRAM(_intPtr, length);
  966. //}
  967. /// <summary>
  968. /// Add sound from byte[] to a DynamicSoundEFFectInstance, it can play while you give it more data.
  969. /// </summary>
  970. /// <param name="decodedStream">sound data</param>
  971. private unsafe void LoadSoundFromStream(ref byte[] buffer, int start, ref int length)
  972. {
  973. if (length > 0 && MediaType == AVMediaType.AVMEDIA_TYPE_AUDIO)
  974. {
  975. if (!useNaudio)
  976. {
  977. if (DynamicSound == null)
  978. {
  979. //create instance here to set sample_rate and channels dynamicly
  980. DynamicSound = new DynamicSoundEffectInstance(ResampleFrame->sample_rate, (AudioChannels)ResampleFrame->channels);
  981. }
  982. try
  983. {
  984. lock (DynamicSoundEffectLock)
  985. DynamicSound.SubmitBuffer(buffer, start, length);
  986. }
  987. catch (ArgumentException)
  988. {
  989. //got error saying buffer was too small. makes no sense.
  990. }
  991. catch (Exception e)
  992. {
  993. if (e.GetType().Name == "SharpDXException")
  994. {
  995. //DynamicSound.Dispose();
  996. DynamicSound = null;
  997. if (!initNaudio())
  998. {
  999. Mode = FfccMode.NOTINIT;
  1000. State = FfccState.NODLL;
  1001. }
  1002. else
  1003. {
  1004. LoadSoundFromStream(ref buffer, start, ref length);
  1005. }
  1006. }
  1007. else
  1008. e.Rethrow();
  1009. }
  1010. }
  1011. else
  1012. RecorderOnDataAvailable(this, new WaveInEventArgs(start > 0 ? buffer.Skip(start).ToArray() : buffer, length - start));
  1013. }
  1014. }
  1015. /// <summary>
  1016. /// Load sound from memory stream by default.
  1017. /// </summary>
  1018. private void LoadSoundFromStream() => LoadSoundFromStream(ref _decodedMemoryStream);
  1019. /// <summary>
  1020. /// If looping seek back to LOOPSTART
  1021. /// </summary>
  1022. private unsafe void Loop()
  1023. {
  1024. if (LOOPSTART >= 0 && Mode == FfccMode.STATE_MACH)
  1025. {
  1026. int min = LOOPSTART - 1000;
  1027. if (min < 0)
  1028. {
  1029. min = 0;
  1030. }
  1031. long max = Decoder.Stream->duration;
  1032. if (max <= LOOPSTART)
  1033. {
  1034. max = LOOPSTART + 1000;
  1035. }
  1036. //I didn't realize this didn't change the framenumber to 0. it just appends the LOOPSTART pos to the current stream.
  1037. //So it is possible this could overflow when it's looped long enough to bring the currentframenum to max value.
  1038. Return = ffmpeg.avformat_seek_file(Decoder.Format, Decoder.StreamIndex, min, LOOPSTART, max, 0);
  1039. CheckReturn();
  1040. State = FfccState.WAITING;
  1041. }
  1042. }
  1043. /// <summary>
  1044. /// For use in threads runs Next till done. To keep audio buffer fed. Or really good timing
  1045. /// on video frames.
  1046. /// </summary>
  1047. private int NextinTask()
  1048. {
  1049. try
  1050. {
  1051. while (Mode == FfccMode.STATE_MACH && !cancellationToken.IsCancellationRequested && State != FfccState.DONE && !IsDisposed)
  1052. {
  1053. lock (Decoder) //make the main thread wait if it accesses this class.
  1054. {
  1055. while (!IsDisposed && !Ahead)
  1056. {
  1057. if (Next() < 0)
  1058. break;
  1059. }
  1060. }
  1061. if (!useNaudio)
  1062. Thread.Sleep(NextAsyncSleep); //delay checks
  1063. }
  1064. }
  1065. //catch (ThreadAbortException)
  1066. //{
  1067. // disposeAll = true;//stop playing
  1068. //}
  1069. finally
  1070. {
  1071. Dispose(cancellationToken.IsCancellationRequested); // dispose of everything except audio encase it's still playing.
  1072. }
  1073. #if _WINDOWS
  1074. if (useNaudio)
  1075. {
  1076. while (!cancellationToken.IsCancellationRequested && nAudioOut != null && nAudioOut.PlaybackState != PlaybackState.Stopped)
  1077. Thread.Sleep(NextAsyncSleep);
  1078. //try
  1079. //{
  1080. // if (nAudioOut != null)
  1081. // nAudioOut.Dispose();
  1082. // bufferedWaveProvider.ClearBuffer();
  1083. //}
  1084. //catch (InvalidOperationException)
  1085. ////{
  1086. // if (nAudioOut != null)
  1087. // Memory.MainThreadOnlyActions.Enqueue(nAudioOut.Dispose);
  1088. // Memory.MainThreadOnlyActions.Enqueue(bufferedWaveProvider.ClearBuffer);
  1089. // //doesn't like threads...
  1090. //}
  1091. }
  1092. #endif
  1093. return 0;
  1094. }
  1095. /// <summary>
  1096. /// Opens filename and assigns FormatContext.
  1097. /// </summary>
  1098. private unsafe int Open()
  1099. {
  1100. if (!FileOpened)
  1101. {
  1102. fixed (AVFormatContext** tmp = &Decoder._format)
  1103. {
  1104. Return = ffmpeg.avformat_open_input(tmp, DecodedFileName, null, null);
  1105. CheckReturn();
  1106. }
  1107. Return = ffmpeg.avformat_find_stream_info(Decoder.Format, null);
  1108. CheckReturn();
  1109. GetTags(ref Decoder.Format->metadata);
  1110. FileOpened = true;
  1111. }
  1112. return (FileOpened) ? 0 : -1;
  1113. }
  1114. /// <summary>
  1115. /// Finds the codec for the chosen stream
  1116. /// </summary>
  1117. private unsafe void PrepareCodec()
  1118. {
  1119. // find & open codec
  1120. fixed (AVCodec** tmp = &Decoder._codec)
  1121. {
  1122. Return = ffmpeg.av_find_best_stream(Decoder.Format, MediaType, -1, -1, tmp, 0);
  1123. }
  1124. if (Return == ffmpeg.AVERROR_STREAM_NOT_FOUND && MediaType == AVMediaType.AVMEDIA_TYPE_AUDIO)
  1125. {
  1126. //Don't fail if no audiostream just be done.
  1127. State = FfccState.DONE;
  1128. Mode = FfccMode.NOTINIT;
  1129. return;
  1130. }
  1131. else
  1132. {
  1133. CheckReturn();
  1134. }
  1135. Decoder.StreamIndex = Return;
  1136. GetTags(ref Decoder.Stream->metadata);
  1137. Decoder.CodecContext = ffmpeg.avcodec_alloc_context3(Decoder.Codec);
  1138. if (Decoder.CodecContext == null)
  1139. {
  1140. die("Could not allocate codec context");
  1141. }
  1142. Return = ffmpeg.avcodec_parameters_to_context(Decoder.CodecContext, Decoder.Stream->codecpar);
  1143. CheckReturn();
  1144. fixed (AVDictionary** tmp = &_dict)
  1145. {
  1146. Return = ffmpeg.av_dict_set(tmp, "strict", "+experimental", 0);
  1147. CheckReturn();
  1148. Return = ffmpeg.avcodec_open2(Decoder.CodecContext, Decoder.Codec, tmp);
  1149. CheckReturn();
  1150. }
  1151. if (MediaType == AVMediaType.AVMEDIA_TYPE_AUDIO)
  1152. {
  1153. if (Decoder.CodecContext->channel_layout == 0)
  1154. {
  1155. if (Decoder.CodecContext->channels == 2)
  1156. {
  1157. Decoder.CodecContext->channel_layout = ffmpeg.AV_CH_LAYOUT_STEREO;
  1158. }
  1159. else if (Decoder.CodecContext->channels == 1)
  1160. {
  1161. Decoder.CodecContext->channel_layout = ffmpeg.AV_CH_LAYOUT_MONO;
  1162. }
  1163. else
  1164. {
  1165. die("must set custom channel layout, is not stereo or mono");
  1166. }
  1167. }
  1168. }
  1169. }
  1170. private void PrepareProcess()
  1171. {
  1172. using (DecodedMemoryStream = new MemoryStream())
  1173. {
  1174. Process();
  1175. LoadSoundFromStream();
  1176. }
  1177. }
  1178. /// <summary>
  1179. /// PrepareResampler
  1180. /// </summary>
  1181. private unsafe void PrepareResampler()
  1182. {
  1183. if (MediaType != AVMediaType.AVMEDIA_TYPE_AUDIO)
  1184. {
  1185. return;
  1186. }
  1187. //resampler
  1188. ResampleFrame = ffmpeg.av_frame_alloc();
  1189. if (ResampleFrame == null)
  1190. {
  1191. die("Error allocating the frame\n");
  1192. }
  1193. ResampleContext = ffmpeg.swr_alloc();
  1194. ffmpeg.av_opt_set_channel_layout(ResampleContext, "in_channel_layout", (long)Decoder.CodecContext->channel_layout, 0);
  1195. ffmpeg.av_opt_set_int(ResampleContext, "in_sample_rate", Decoder.CodecContext->sample_rate, 0);
  1196. ffmpeg.av_opt_set_sample_fmt(ResampleContext, "in_sample_fmt", Decoder.CodecContext->sample_fmt, 0);
  1197. ffmpeg.av_opt_set_channel_layout(ResampleContext, "out_channel_layout", (long)Decoder.CodecContext->channel_layout, 0);
  1198. ffmpeg.av_opt_set_sample_fmt(ResampleContext, "out_sample_fmt", AVSampleFormat.AV_SAMPLE_FMT_S16, 0);
  1199. ffmpeg.av_opt_set_int(ResampleContext, "out_sample_rate", Decoder.CodecContext->sample_rate, 0);
  1200. Return = ffmpeg.swr_init(ResampleContext);
  1201. if (Return < 0)
  1202. {
  1203. die("swr_init");
  1204. }
  1205. Decoder.Frame->format = (int)Decoder.CodecContext->sample_fmt;
  1206. Decoder.Frame->channel_layout = Decoder.CodecContext->channel_layout;
  1207. Decoder.Frame->channels = Decoder.CodecContext->channels;
  1208. Decoder.Frame->sample_rate = Decoder.CodecContext->sample_rate;
  1209. ResampleFrame->nb_samples = Decoder.CodecContext->frame_size;
  1210. if (ResampleFrame->nb_samples == 0)
  1211. {
  1212. ResampleFrame->nb_samples = 32; //32, or 64, ADPCM require 32.
  1213. }
  1214. ResampleFrame->format = (int)AVSampleFormat.AV_SAMPLE_FMT_S16;
  1215. ResampleFrame->channel_layout = Decoder.CodecContext->channel_layout;
  1216. ResampleFrame->channels = Decoder.CodecContext->channels;
  1217. ResampleFrame->sample_rate = Decoder.CodecContext->sample_rate;
  1218. int convertedFrameBufferSize = ffmpeg.av_samples_get_buffer_size(null, ResampleFrame->channels,
  1219. ResampleFrame->nb_samples,
  1220. (AVSampleFormat)ResampleFrame->format, 0);
  1221. //ConvertedData = (byte*)Marshal.AllocHGlobal(convertedFrameBufferSize);
  1222. ConvertedData = new byte[convertedFrameBufferSize];
  1223. if (useNaudio) initNaudio();
  1224. }
  1225. /// <summary>
  1226. /// Setup scaler for drawing frames to bitmap.
  1227. /// </summary>
  1228. private unsafe void PrepareScaler()
  1229. {
  1230. if (MediaType != AVMediaType.AVMEDIA_TYPE_VIDEO)
  1231. {
  1232. return;
  1233. }
  1234. ScalerContext = ffmpeg.sws_getContext(
  1235. Decoder.CodecContext->width, Decoder.CodecContext->height, Decoder.CodecContext->pix_fmt,
  1236. Decoder.CodecContext->width, Decoder.CodecContext->height, AVPixelFormat.AV_PIX_FMT_RGBA,
  1237. ffmpeg.SWS_ACCURATE_RND, null, null, null);
  1238. Return = ffmpeg.sws_init_context(ScalerContext, null, null);
  1239. CheckReturn();
  1240. }
  1241. /// <summary>
  1242. /// Decodes, Resamples, Encodes
  1243. /// </summary>
  1244. /// <ref>https://stackoverflow.com/questions/32051847/c-ffmpeg-distorted-sound-when-converting-audio?rq=1#_=_</ref>
  1245. private unsafe void Process()
  1246. {
  1247. while (Decode(out AVFrame _DecodedFrame))
  1248. {
  1249. if (MediaType == AVMediaType.AVMEDIA_TYPE_VIDEO)
  1250. {
  1251. if (Mode == FfccMode.STATE_MACH)
  1252. {
  1253. State = FfccState.WAITING;
  1254. break;
  1255. }
  1256. // do something with video here.
  1257. else if (Mode == FfccMode.PROCESS_ALL)
  1258. {
  1259. }
  1260. }
  1261. else if (MediaType == AVMediaType.AVMEDIA_TYPE_AUDIO)
  1262. {
  1263. Resample(ref _DecodedFrame);
  1264. if (Mode == FfccMode.STATE_MACH && !Behind) //still behind stay here.
  1265. {
  1266. State = FfccState.WAITING;
  1267. break;
  1268. }
  1269. }
  1270. }
  1271. if (State != FfccState.WAITING)
  1272. {
  1273. State = FfccState.DONE;
  1274. timer.Stop();
  1275. timer.Reset();
  1276. DecodeFlush(ref Decoder._codecContext, ref *Decoder.Packet); //calling this twice was causing issues.
  1277. }
  1278. }
  1279. /// <summary>
  1280. /// </summary>
  1281. /// <param name="sender"></param>
  1282. /// <param name="waveInEventArgs"></param>
  1283. /// <see cref="https://markheath.net/post/how-to-record-and-play-audio-at-same"/>
  1284. private void RecorderOnDataAvailable(object sender, WaveInEventArgs waveInEventArgs)
  1285. {
  1286. #if _WINDOWS
  1287. if (bufferedWaveProvider == null)
  1288. initNaudio();
  1289. if (useNaudio)
  1290. bufferedWaveProvider.AddSamples(waveInEventArgs.Buffer, 0, waveInEventArgs.BytesRecorded);
  1291. #endif
  1292. }
  1293. /// <summary>
  1294. /// Resample current frame, Save output data
  1295. /// </summary>
  1296. /// <param name="frame">Current Decoded Frame</param>
  1297. private unsafe void Resample(ref AVFrame frame)
  1298. {
  1299. // Convert
  1300. int outSamples = 0;
  1301. fixed (byte** tmp = (byte*[])frame.data)
  1302. {
  1303. outSamples = ffmpeg.swr_convert(ResampleContext, null, 0, tmp, frame.nb_samples);
  1304. }
  1305. if (outSamples < 0)
  1306. {
  1307. die("Could not convert");
  1308. }
  1309. for (; ; )
  1310. {
  1311. outSamples = ffmpeg.swr_get_out_samples(ResampleContext, 0);
  1312. // 32 was nb_samples but if too big would just not decode anything
  1313. if (outSamples < 32 * ResampleFrame->channels)
  1314. {
  1315. break;
  1316. }
  1317. //fixed (byte** tmp = &_convertedData)
  1318. fixed (byte* tmp = &_convertedData[0])
  1319. {
  1320. outSamples = ffmpeg.swr_convert(ResampleContext, &tmp, ResampleFrame->nb_samples, null, 0);
  1321. }
  1322. int buffer_size = ffmpeg.av_samples_get_buffer_size(null,
  1323. ResampleFrame->channels,
  1324. ResampleFrame->nb_samples,
  1325. (AVSampleFormat)ResampleFrame->format,
  1326. 0);
  1327. // write to buffer
  1328. WritetoMs(ref _convertedData, 0, ref buffer_size);
  1329. }
  1330. }
  1331. /// <summary>
  1332. /// Run code depending on state
  1333. /// </summary>
  1334. /// <param name="state">Change the state to this</param>
  1335. /// <param name="ret">return this</param>
  1336. /// <returns>ret</returns>
  1337. private int Update(FfccState state = FfccState.NULL, int ret = 0)
  1338. {
  1339. if (Mode == FfccMode.NOTINIT)
  1340. {
  1341. die("Class not Init");
  1342. }
  1343. if (state == FfccState.NODLL)
  1344. {
  1345. return -1;
  1346. }
  1347. if (state != FfccState.NULL)
  1348. {
  1349. State = state;
  1350. }
  1351. do
  1352. {
  1353. switch (State)
  1354. {
  1355. case FfccState.OPEN:
  1356. State = FfccState.GETCODEC;
  1357. if (0 < Open())
  1358. {
  1359. die("Failed to Open");
  1360. }
  1361. break;
  1362. case FfccState.GETCODEC:
  1363. State = FfccState.PREPARE_SWR;
  1364. PrepareCodec();
  1365. break;
  1366. case FfccState.PREPARE_SWR:
  1367. State = FfccState.PREPARE_SWS;
  1368. PrepareResampler();
  1369. break;
  1370. case FfccState.PREPARE_SWS:
  1371. State = FfccState.READ;
  1372. PrepareScaler();
  1373. break;
  1374. case FfccState.READ://Enters waiting state unless we want to process all now.
  1375. State = Mode == FfccMode.PROCESS_ALL ? FfccState.READALL : FfccState.READONE;
  1376. //READONE here makes it grab one video frame and precaches audio to it's limit.
  1377. //WAITING here makes it wait till GetFrame() is called.
  1378. //READALL just processes the whole audio stream (memoryleaky), not ment for video
  1379. //Currently video will just saves all the frames to bmp in temp folder on READALL.
  1380. break;
  1381. case FfccState.READALL:
  1382. State = FfccState.DONE;
  1383. PrepareProcess();
  1384. break;
  1385. case FfccState.READONE:
  1386. PrepareProcess();
  1387. switch (State)
  1388. {
  1389. case FfccState.WAITING:
  1390. ret = 0;
  1391. break;
  1392. default:
  1393. ret = -1;
  1394. break;
  1395. }
  1396. break;
  1397. default:
  1398. State = FfccState.DONE;
  1399. break;
  1400. }
  1401. }
  1402. while (!((Mode == FfccMode.PROCESS_ALL && State == FfccState.DONE) || (State == FfccState.DONE || State == FfccState.WAITING)));
  1403. return ret;
  1404. }
  1405. private int WritetoMs(ref byte[] output, int start, ref int length)
  1406. {
  1407. if (Mode == FfccMode.STATE_MACH)
  1408. {
  1409. LoadSoundFromStream(ref output, start, ref length);
  1410. }
  1411. else
  1412. {
  1413. DecodedMemoryStream.Write(output, start, length);
  1414. }
  1415. return length - start;
  1416. }
  1417. /// <summary>
  1418. /// Write to Memory Stream.
  1419. /// </summary>
  1420. /// <param name="output">Byte pointer to output buffer array</param>
  1421. /// <param name="start">Start from typically 0</param>
  1422. /// <param name="length">Total bytes to read.</param>
  1423. /// <returns>bytes wrote</returns>
  1424. private unsafe int WritetoMs(ref byte* output, int start, ref int length)
  1425. {
  1426. if (Mode == FfccMode.STATE_MACH)
  1427. {
  1428. byte[] arr = new byte[length];
  1429. Marshal.Copy((IntPtr)output, arr, 0, length);
  1430. LoadSoundFromStream(ref arr, start, ref length);
  1431. return length;
  1432. }
  1433. else
  1434. {
  1435. //memory leaky? seems when i used this method alot of memory wouldn't get disposed, might be only with large sounds
  1436. long c_len = DecodedMemoryStream.Length;
  1437. for (int i = start; i < length; i++)
  1438. {
  1439. DecodedMemoryStream.WriteByte(output[i]);
  1440. }
  1441. if (DecodedMemoryStream.Length - c_len != length)
  1442. {
  1443. die("not all data wrote");
  1444. }
  1445. return (int)(DecodedMemoryStream.Length - c_len);
  1446. }
  1447. }
  1448. #endregion Methods
  1449. }
  1450. }