FfmpegCalls.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. using System.Diagnostics;
  2. using System.Reflection;
  3. using System.Runtime.InteropServices;
  4. using Bmp.Core.FFMpeg.CsCorePorts.FFMpegWrap.Interops;
  5. namespace Bmp.Core.FFMpeg.CsCorePorts.FFMpegWrap;
  6. internal class FfmpegCalls
  7. {
  8. [Flags]
  9. public enum SeekFlags
  10. {
  11. SeekSet = 0,
  12. SeekCur = 1,
  13. SeekEnd = 2,
  14. SeekSize = 0x10000,
  15. SeekForce = 0x20000
  16. }
  17. [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
  18. public delegate int AvioReadData(nint opaque, nint buffer, int bufferSize);
  19. [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
  20. public delegate int AvioWriteData(nint opaque, nint buffer, int bufferSize);
  21. [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
  22. public delegate long AvioSeek(nint opaque, long offset, SeekFlags whence);
  23. static FfmpegCalls()
  24. {
  25. string platform;
  26. switch (Environment.OSVersion.Platform)
  27. {
  28. case PlatformID.Win32NT:
  29. case PlatformID.Win32S:
  30. case PlatformID.Win32Windows:
  31. platform = "windows";
  32. break;
  33. case PlatformID.Unix:
  34. case PlatformID.MacOSX:
  35. platform = "unix";
  36. break;
  37. default:
  38. throw new PlatformNotSupportedException();
  39. }
  40. var assemblyDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
  41. if (assemblyDirectory != null)
  42. {
  43. var path = Path.Combine(
  44. assemblyDirectory,
  45. //"FFmpeg"
  46. Path.Combine("FFmpeg", Path.Combine("bin", Path.Combine(platform, nint.Size == 8 ? "x64" : "x86")))
  47. );
  48. InteropHelper.RegisterLibrariesSearchPath(path);
  49. }
  50. //FfmpegConfigurationSection ffmpegSettings =
  51. // (FfmpegConfigurationSection)ConfigurationManager.GetSection("ffmpeg");
  52. //if (ffmpegSettings != null)
  53. //{
  54. // if (!String.IsNullOrEmpty(ffmpegSettings.HttpProxy))
  55. // {
  56. // Environment.SetEnvironmentVariable("http_proxy", ffmpegSettings.HttpProxy);
  57. // Environment.SetEnvironmentVariable("no_proxy", ffmpegSettings.ProxyWhitelist);
  58. // }
  59. // if (ffmpegSettings.LogLevel != null)
  60. // {
  61. // FfmpegUtils.LogLevel = ffmpegSettings.LogLevel.Value;
  62. // }
  63. //}
  64. FFMPEG.av_register_all();
  65. FFMPEG.avcodec_register_all();
  66. FFMPEG.avformat_network_init();
  67. }
  68. internal static unsafe AVOutputFormat[] GetOutputFormats()
  69. {
  70. var formats = new List<AVOutputFormat>();
  71. var format = FFMPEG.av_oformat_next(null);
  72. while (format != null)
  73. {
  74. formats.Add(*format);
  75. format = FFMPEG.av_oformat_next(format);
  76. }
  77. return formats.ToArray();
  78. }
  79. internal static unsafe AVInputFormat[] GetInputFormats()
  80. {
  81. var formats = new List<AVInputFormat>();
  82. var format = FFMPEG.av_iformat_next(null);
  83. while (format != null)
  84. {
  85. formats.Add(*format);
  86. format = FFMPEG.av_iformat_next(format);
  87. }
  88. return formats.ToArray();
  89. }
  90. internal static unsafe List<AvCodecId> GetCodecOfCodecTag(AVCodecTag** codecTag)
  91. {
  92. var codecs = new List<AvCodecId>();
  93. uint i = 0;
  94. AvCodecId codecId;
  95. while ((codecId = FFMPEG.av_codec_get_id(codecTag, i++)) != AvCodecId.None)
  96. {
  97. codecs.Add(codecId);
  98. }
  99. return codecs;
  100. }
  101. internal static unsafe nint AvMalloc(int bufferSize)
  102. {
  103. var buffer = FFMPEG.av_malloc((ulong)bufferSize);
  104. var ptr = new nint(buffer);
  105. if (ptr == nint.Zero)
  106. throw new OutOfMemoryException("Could not allocate memory.");
  107. return ptr;
  108. }
  109. internal static unsafe void AvFree(nint buffer)
  110. {
  111. FFMPEG.av_free((void*)buffer);
  112. }
  113. internal static unsafe AVIOContext* AvioAllocContext(AvioBuffer buffer, bool writeable, nint userData,
  114. AvioReadData readData, AvioWriteData writeData, AvioSeek seek)
  115. {
  116. var bufferPtr = (byte*)buffer.Buffer;
  117. var avioContext = FFMPEG.avio_alloc_context(
  118. bufferPtr,
  119. buffer.BufferSize,
  120. writeable ? 1 : 0,
  121. (void*)userData,
  122. readData, writeData, seek);
  123. if (avioContext == null)
  124. {
  125. throw new FfmpegException("Could not allocate avio-context.", "avio_alloc_context");
  126. }
  127. return avioContext;
  128. }
  129. internal static unsafe AVFormatContext* AvformatAllocContext()
  130. {
  131. var formatContext = FFMPEG.avformat_alloc_context();
  132. if (formatContext == null)
  133. {
  134. throw new FfmpegException("Could not allocate avformat-context.", "avformat_alloc_context");
  135. }
  136. return formatContext;
  137. }
  138. internal static unsafe void AvformatOpenInput(AVFormatContext** formatContext, AvioContext avioContext)
  139. {
  140. (*formatContext)->pb = (AVIOContext*)avioContext.ContextPtr;
  141. var result = FFMPEG.avformat_open_input(formatContext, "DUMMY-FILENAME", null, null);
  142. FfmpegException.Try(result, "avformat_open_input");
  143. }
  144. internal static unsafe void AvformatOpenInput(AVFormatContext** formatContext, string url)
  145. {
  146. var result = FFMPEG.avformat_open_input(formatContext, url, null, null);
  147. FfmpegException.Try(result, "avformat_open_input");
  148. }
  149. internal static unsafe void AvformatCloseInput(AVFormatContext** formatContext)
  150. {
  151. FFMPEG.avformat_close_input(formatContext);
  152. }
  153. internal static unsafe void AvFormatFindStreamInfo(AVFormatContext* formatContext)
  154. {
  155. var result = FFMPEG.avformat_find_stream_info(formatContext, null);
  156. FfmpegException.Try(result, "avformat_find_stream_info");
  157. }
  158. internal static unsafe int AvFindBestStreamInfo(AVFormatContext* formatContext)
  159. {
  160. var result = FFMPEG.av_find_best_stream(
  161. formatContext,
  162. AVMediaType.AVMEDIA_TYPE_AUDIO,
  163. -1, -1, null, 0);
  164. FfmpegException.Try(result, "av_find_best_stream");
  165. return result; //stream index
  166. }
  167. internal static unsafe AVCodec* AvCodecFindDecoder(AvCodecId codecId)
  168. {
  169. var decoder = FFMPEG.avcodec_find_decoder(codecId);
  170. if (decoder == null)
  171. {
  172. throw new FfmpegException(
  173. string.Format("Failed to find a decoder for CodecId {0}.", codecId),
  174. "avcodec_find_decoder");
  175. }
  176. return decoder;
  177. }
  178. internal static unsafe void AvCodecOpen(AVCodecContext* codecContext, AVCodec* codec)
  179. {
  180. var result = FFMPEG.avcodec_open2(codecContext, codec, null);
  181. FfmpegException.Try(result, "avcodec_open2");
  182. }
  183. internal static unsafe void AvCodecClose(AVCodecContext* codecContext)
  184. {
  185. FFMPEG.avcodec_close(codecContext);
  186. }
  187. internal static unsafe AVFrame* AvFrameAlloc()
  188. {
  189. var frame = FFMPEG.av_frame_alloc();
  190. if (frame == null)
  191. {
  192. throw new FfmpegException("Could not allocate frame.", "av_frame_alloc");
  193. }
  194. return frame;
  195. }
  196. internal static unsafe void AvFrameFree(AVFrame* frame)
  197. {
  198. FFMPEG.av_frame_free(&frame);
  199. }
  200. internal static unsafe void InitPacket(AVPacket* packet)
  201. {
  202. FFMPEG.av_init_packet(packet);
  203. }
  204. internal static unsafe void FreePacket(AVPacket* packet)
  205. {
  206. FFMPEG.av_packet_unref(packet);
  207. }
  208. internal static unsafe bool AvReadFrame(AvFormatContext formatContext, AVPacket* packet)
  209. {
  210. var result = FFMPEG.av_read_frame((AVFormatContext*)formatContext.FormatPtr, packet);
  211. return result >= 0;
  212. }
  213. internal static unsafe bool AvCodecDecodeAudio4(AVCodecContext* codecContext, AVFrame* frame, AVPacket* packet, out int bytesConsumed)
  214. {
  215. int gotFrame;
  216. var result = FFMPEG.avcodec_decode_audio4(codecContext, frame, &gotFrame, packet);
  217. FfmpegException.Try(result, "avcodec_decode_audio4");
  218. bytesConsumed = result;
  219. return gotFrame != 0;
  220. }
  221. internal static int AvGetBytesPerSample(AVSampleFormat sampleFormat)
  222. {
  223. var dataSize = FFMPEG.av_get_bytes_per_sample(sampleFormat);
  224. if (dataSize <= 0)
  225. {
  226. throw new FfmpegException("Could not calculate data size.");
  227. }
  228. return dataSize;
  229. }
  230. internal static unsafe int AvSamplesGetBufferSize(AVFrame* frame)
  231. {
  232. var result = FFMPEG.av_samples_get_buffer_size(null, frame->channels, frame->nb_samples,
  233. (AVSampleFormat)frame->format, 1);
  234. FfmpegException.Try(result, "av_samples_get_buffer_size");
  235. return result;
  236. }
  237. internal static unsafe void AvFormatSeekFile(AvFormatContext formatContext, double time)
  238. {
  239. var result = FFMPEG.avformat_seek_file((AVFormatContext*)formatContext.FormatPtr,
  240. formatContext.BestAudioStreamIndex, long.MinValue, (long)time, (long)time, 0);
  241. FfmpegException.Try(result, "avformat_seek_file");
  242. }
  243. internal static unsafe string AvStrError(int errorCode)
  244. {
  245. var buffer = stackalloc byte[500];
  246. var result = FFMPEG.av_strerror(errorCode, new nint(buffer), 500);
  247. if (result < 0)
  248. return "No description available.";
  249. var errorMessage = Marshal.PtrToStringAnsi(new nint(buffer), 500).Trim('\0').Trim();
  250. #if DEBUG
  251. Debug.WriteLineIf(Debugger.IsAttached, errorMessage);
  252. #endif
  253. return errorMessage;
  254. }
  255. internal static void SetLogLevel(LogLevel level)
  256. {
  257. FFMPEG.av_log_set_level((int)level);
  258. }
  259. internal static LogLevel GetLogLevel()
  260. {
  261. return (LogLevel)FFMPEG.av_log_get_level();
  262. }
  263. internal unsafe delegate void LogCallback(void* ptr, int level, byte* fmt, nint vl);
  264. internal static unsafe void SetLogCallback(LogCallback callback)
  265. {
  266. FFMPEG.av_log_set_callback(Marshal.GetFunctionPointerForDelegate(callback));
  267. }
  268. internal static unsafe LogCallback GetDefaultLogCallback()
  269. {
  270. return (ptr, level, fmt, vl) =>
  271. {
  272. FFMPEG.av_log_default_callback(ptr, level, Marshal.PtrToStringAnsi(new nint(fmt)), (sbyte*)vl);
  273. };
  274. }
  275. internal static unsafe string FormatLine(void* avcl, int level, string fmt, nint vl,
  276. ref int printPrefix)
  277. {
  278. var line = string.Empty;
  279. const int bufferSize = 0x400;
  280. var buffer = stackalloc byte[bufferSize];
  281. fixed (int* ppp = &printPrefix)
  282. {
  283. var result = FFMPEG.av_log_format_line2(avcl, level, fmt, (sbyte*)vl, (nint)buffer, bufferSize, ppp);
  284. if (result < 0)
  285. {
  286. Debug.WriteLine("av_log_format_line2 failed with " + result.ToString("x8"));
  287. return line;
  288. }
  289. line = Marshal.PtrToStringAnsi((nint)buffer);
  290. if (line != null && result > 0)
  291. line = line.Substring(0, result);
  292. return line;
  293. }
  294. }
  295. }