Skip to content

Commit

Permalink
Support for ffmpeg logging and enumerating support formats. See filoe…
Browse files Browse the repository at this point in the history
  • Loading branch information
filoe committed Jan 21, 2017
1 parent a12154f commit 9ad3635
Show file tree
Hide file tree
Showing 7 changed files with 417 additions and 48 deletions.
5 changes: 5 additions & 0 deletions CSCore.Ffmpeg/CSCore.Ffmpeg.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
Expand All @@ -45,11 +46,14 @@
<Compile Include="AvioContext.cs" />
<Compile Include="AvStream.cs" />
<Compile Include="FfmpegCalls.cs" />
<Compile Include="FfmpegConfigurationSection.cs" />
<Compile Include="FfmpegDecoder.cs" />
<Compile Include="FfmpegException.cs" />
<Compile Include="FfmpegLogReceivedEventArgs.cs" />
<Compile Include="FfmpegStream.cs" />
<Compile Include="FfmpegUtils.cs" />
<Compile Include="AvCodecId.cs" />
<Compile Include="Format.cs" />
<Compile Include="Interops\ConstCharPtrMarshaler.cs" />
<Compile Include="Interops\FFmpeg.avcodec.g.cs" />
<None Include="Interops\unused\FFmpeg.avdevice.g.cs" />
Expand All @@ -62,6 +66,7 @@
<None Include="Interops\unused\FFmpeg.swscale.g.cs" />
<Compile Include="Interops\InteropHelper.cs" />
<Compile Include="Interops\Unresolved.cs" />
<Compile Include="LogLevel.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
Expand Down
78 changes: 73 additions & 5 deletions CSCore.Ffmpeg/FfmpegCalls.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using CSCore.Ffmpeg.Interops;
Expand Down Expand Up @@ -49,14 +51,31 @@ static FfmpegCalls()
{
string path = Path.Combine(
assemblyDirectory,
Path.Combine("FFmpeg", Path.Combine("bin",
Path.Combine("FFmpeg", Path.Combine("bin",
Path.Combine(platform, IntPtr.Size == 8 ? "x64" : "x86"))));

InteropHelper.RegisterLibrariesSearchPath(path);
}

FfmpegConfigurationSection ffmpegSettings =
(FfmpegConfigurationSection)ConfigurationManager.GetSection("ffmpeg");
if (ffmpegSettings != null)
{
if (!String.IsNullOrEmpty(ffmpegSettings.HttpProxy))
{
Environment.SetEnvironmentVariable("http_proxy", ffmpegSettings.HttpProxy);
Environment.SetEnvironmentVariable("no_proxy", ffmpegSettings.ProxyWhitelist);
}
if (ffmpegSettings.LogLevel != null)
{
FfmpegUtils.LogLevel = ffmpegSettings.LogLevel.Value;
}
}

ffmpeg.av_register_all();
ffmpeg.avcodec_register_all();

ffmpeg.avformat_network_init();
}

internal static unsafe AVOutputFormat[] GetOutputFormats()
Expand Down Expand Up @@ -104,21 +123,21 @@ internal static unsafe IntPtr AvMalloc(int bufferSize)
{
void* buffer = ffmpeg.av_malloc((ulong) bufferSize);
IntPtr ptr = new IntPtr(buffer);
if(ptr == IntPtr.Zero)
if (ptr == IntPtr.Zero)
throw new OutOfMemoryException("Could not allocate memory.");
return ptr;
}


internal static unsafe void AvFree(IntPtr buffer)
{
ffmpeg.av_free((void*)buffer);
ffmpeg.av_free((void*) buffer);
}

internal static unsafe AVIOContext* AvioAllocContext(AvioBuffer buffer, bool writeable, IntPtr userData,
AvioReadData readData, AvioWriteData writeData, AvioSeek seek)
{
byte* bufferPtr = (byte*)buffer.Buffer;
byte* bufferPtr = (byte*) buffer.Buffer;

var avioContext = ffmpeg.avio_alloc_context(
bufferPtr,
Expand Down Expand Up @@ -235,7 +254,8 @@ internal static unsafe bool AvReadFrame(AvFormatContext formatContext, AVPacket*
return result >= 0;
}

internal static unsafe bool AvCodecDecodeAudio4(AVCodecContext* codecContext, AVFrame* frame, AVPacket* packet, out int bytesConsumed)
internal static unsafe bool AvCodecDecodeAudio4(AVCodecContext* codecContext, AVFrame* frame, AVPacket* packet,
out int bytesConsumed)
{
int gotFrame;
int result = ffmpeg.avcodec_decode_audio4(codecContext, frame, &gotFrame, packet);
Expand Down Expand Up @@ -282,5 +302,53 @@ internal static unsafe string AvStrError(int errorCode)
#endif
return errorMessage;
}

internal static void SetLogLevel(LogLevel level)
{
ffmpeg.av_log_set_level((int) level);
}

internal static LogLevel GetLogLevel()
{
return (LogLevel) ffmpeg.av_log_get_level();
}

internal unsafe delegate void LogCallback(void* ptr, int level, byte* fmt, IntPtr vl);

internal static unsafe void SetLogCallback(LogCallback callback)
{
ffmpeg.av_log_set_callback(Marshal.GetFunctionPointerForDelegate(callback));
}

internal static unsafe LogCallback GetDefaultLogCallback()
{
return (ptr, level, fmt, vl) =>
{
ffmpeg.av_log_default_callback(ptr, level, Marshal.PtrToStringAnsi(new IntPtr(fmt)), (sbyte*) vl);
};
}

internal static unsafe string FormatLine(void* avcl, int level, string fmt, IntPtr vl,
ref int printPrefix)
{
string line = String.Empty;

const int bufferSize = 0x400;
byte* buffer = stackalloc byte[bufferSize];
fixed (int* ppp = &printPrefix)
{
int result = ffmpeg.av_log_format_line2(avcl, level, fmt, (sbyte*) vl, (IntPtr) buffer, bufferSize, ppp);
if (result < 0)
{
Debug.WriteLine("av_log_format_line2 failed with " + result.ToString("x8"));
return line;
}

line = Marshal.PtrToStringAnsi((IntPtr)buffer);
if (line != null && result > 0)
line = line.Substring(0, result);
return line;
}
}
}
}
58 changes: 58 additions & 0 deletions CSCore.Ffmpeg/FfmpegConfigurationSection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System;
using System.Configuration;

namespace CSCore.Ffmpeg
{
/// <summary>
/// Encapsulates configuration properties for CSCore.Ffmpeg.
/// </summary>
public class FfmpegConfigurationSection : ConfigurationSection
{
/// <summary>
/// Gets or sets the HTTP proxy.
/// </summary>
/// <value>
/// A proxy with the following format: http://[User@]MyProxy.MyDomain:Port/.
/// </value>
[ConfigurationProperty("httpProxy", DefaultValue = "", IsRequired = false)]
public string HttpProxy
{
get { return (string)this["httpProxy"]; }
set { this["httpProxy"] = value; }
}

/// <summary>
/// Gets or sets a whitelist where no proxy should be used.
/// </summary>
/// <value>
/// The proxy whitelist. For examples see https://ffmpeg.org/doxygen/3.2/noproxy_8c_source.html.
/// </value>
[ConfigurationProperty("proxyWhitelist", DefaultValue = "*", IsRequired = false)]
public string ProxyWhitelist
{
get { return (string) this["proxyWhitelist"]; }
set { this["proxyWhitelist"] = value; }
}

/// <summary>
/// Gets or sets the log level. For more details see <see cref="FfmpegUtils.LogLevel"/>.
/// </summary>
/// <value>
/// The log level.
/// </value>
[ConfigurationProperty("loglevel", DefaultValue = null, IsRequired = false)]
public LogLevel? LogLevel
{
get
{
var obj = this["loglevel"];
if (obj is LogLevel && Enum.IsDefined(typeof (LogLevel), obj))
{
return (LogLevel?) obj;
}
return null;
}
set { this["loglevel"] = value; }
}
}
}
98 changes: 98 additions & 0 deletions CSCore.Ffmpeg/FfmpegLogReceivedEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using System;
using System.Runtime.InteropServices;
using CSCore.Ffmpeg.Interops;

namespace CSCore.Ffmpeg
{
/// <summary>
/// Provides data for the <see cref="FfmpegUtils.FfmpegLogReceived"/> event.
/// </summary>
public class FfmpegLogReceivedEventArgs : EventArgs
{
/// <summary>
/// Gets the message.
/// </summary>
/// <value>
/// The message.
/// </value>
public string Message { get; private set; }

/// <summary>
/// Gets the level of the message.
/// </summary>
/// <value>
/// The level of the message.
/// </value>
public LogLevel Level { get; private set; }

/// <summary>
/// Gets the name of the class.
/// </summary>
/// <value>
/// The name of the class.
/// </value>
public string ClassName { get; private set; }

/// <summary>
/// Gets the item name of the class.
/// </summary>
/// <value>
/// The item name of the class.
/// </value>
public string ItemName { get; private set; }

/// <summary>
/// Gets or sets the name of the parent log context class.
/// </summary>
/// <value>
/// The name of the parent log context class. Might me empty.
/// </value>
public string ParentLogContextClassName { get; set; }

/// <summary>
/// Gets or sets the item name of the parent log context class.
/// </summary>
/// <value>
/// The item name of the parent log context class. Might me empty.
/// </value>
public string ParentLogContextItemName { get; set; }

private delegate IntPtr ItemNameFunc(IntPtr avClass);

internal unsafe FfmpegLogReceivedEventArgs(AVClass? avClass, AVClass? parentLogContext, LogLevel level, string line, void* ptr, void* ptr1)
{
ItemNameFunc itemNameFunc;
IntPtr strPtr;

Message = line;
Level = level;

if (avClass != null)
{
AVClass avc = avClass.Value;

ClassName = Marshal.PtrToStringAnsi((IntPtr)avc.class_name);
if (avc.item_name != IntPtr.Zero)
{
itemNameFunc = (ItemNameFunc) Marshal.GetDelegateForFunctionPointer(avc.item_name, typeof(ItemNameFunc));
strPtr = itemNameFunc((IntPtr)ptr);
if (strPtr != IntPtr.Zero)
ItemName = Marshal.PtrToStringAnsi(strPtr);
}
}
if (parentLogContext != null)
{
AVClass pavc = parentLogContext.Value;

ParentLogContextClassName = Marshal.PtrToStringAnsi((IntPtr)pavc.class_name);
if (pavc.item_name != IntPtr.Zero)
{
itemNameFunc = (ItemNameFunc) Marshal.GetDelegateForFunctionPointer(pavc.item_name, typeof(ItemNameFunc));
strPtr = itemNameFunc((IntPtr) ptr1);
if (strPtr != IntPtr.Zero)
ParentLogContextItemName = Marshal.PtrToStringAnsi(strPtr);
}
}
}
}
}
Loading

0 comments on commit 9ad3635

Please sign in to comment.