Skip to content

Commit

Permalink
Upgrade to FFmpeg 4 (#228)
Browse files Browse the repository at this point in the history
* upgrading to FFmperg 4

* bumping up date version

* Continued work on HW acceleration

* Improved hardware acceleration decoding APIs

* minor changes for HW init stuff.

* device type name and device pixel format name

* Minor enhancements for decodiing cycle and videoblock allocation logic

* Adding methods to iterate over options availability. still work to do to pass those options!

* Minor adjustment to deinterlace filter detection and addressing issue #220

* Enhancing codec options retrieval.

* Exposing Options metadata via MediaEngine

* Completed fixing demuxer global and private options. Still need to work on strea-specific options

* minor comment added.

* simplifying decoder options api

* Minor refactoring. still trying to figure out how to pass some options to the hardware codec.

* more work on built-in hardware decoders.

* minor improvements and adjustments to hardware APIs

* video filters for hardware acceleration support. Exchange frame before filtering. Alos addressing issue #224

* Video filter appender.

* minor bugfix

* closes issue #216

* An attempt to perform image composition on a separate thread. Main Change is in VideoRenderer but it is not complete! See issue #185

* More experiments with separate visual tree

* better hostedimage element

* continued work on thread separted hosted image

* attempting to recover deginer

* Better ElementHost

* Improved element host

* Minor improvements to video renderer

* upgrading to FFmpeg.AutoGen 4.0.0.2
  • Loading branch information
mariodivece committed May 16, 2018
1 parent ddc77c6 commit fd78438
Show file tree
Hide file tree
Showing 60 changed files with 1,900 additions and 957 deletions.
6 changes: 3 additions & 3 deletions README.md
Expand Up @@ -10,7 +10,7 @@

![ffmeplay](https://github.com/unosquare/ffmediaelement/raw/master/Support/ffmeplay.png)

## Announcements
## Current Release Status
- If you would like to support this project, you can show your appreciation via [PayPal.Me](https://www.paypal.me/mariodivece/50usd)
- Current Status: (2018-04-25) - Release 3.4.240 is now available, (see the <a href="https://github.com/unosquare/ffmediaelement/releases">Releases</a>)
- NuGet Package available here: https://www.nuget.org/packages/FFME.Windows/
Expand All @@ -21,7 +21,7 @@
Here is a quick guide on how to get started.
1. Open Visual Studio (v2017 recommended), and create a new WPF Application. Target Framework must be 4.6.2 or above. (This will change to 4.6.1 in the final release)
2. Install the NuGet Package from your Package Manager Console: `PM> Install-Package FFME.Windows`
3. You need FFmpeg binaries now. Build your own or download a compatible build from [Zeranoe FFmpeg Builds site](https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-3.4.2-win32-shared.zip).
3. You need FFmpeg binaries now. Build your own or download a compatible build from [Zeranoe FFmpeg Builds site](https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-4.0-win32-shared.zip).
4. Your FFmpeg build should have a `bin` folder with 3 exe files and 8 dll files. Copy all 11 files to a folder such as `c:\ffmpeg`
5. Within you application's startup code (`Main` method), set `Unosquare.FFME.MediaElement.FFmpegDirectory = @"c:\ffmpeg";`.
6. Use the FFME `MediaElement` control as any other WPF control.
Expand Down Expand Up @@ -82,7 +82,7 @@ device://gdigrab?desktop
*Please note that I am unable to distribute FFmpeg's binaries because I don't know if I am allowed to do so. Follow the instructions below to compile, run and test FFME.*

1. Clone this repository.
2. Download the FFmpeg win32-shared binaries from <a href="https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-3.4.2-win32-shared.zip">Zeranoe FFmpeg Builds</a>.
2. Download the FFmpeg win32-shared binaries from <a href="https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-4.0-win32-shared.zip">Zeranoe FFmpeg Builds</a>.
3. Extract the contents of the <code>zip</code> file you just downloaded and go to the <code>bin</code> folder that got extracted. You should see 3 <code>exe</code> files and 8 <code>dll</code> files. Select and copy all of them.
4. Now paste all 11 files from the prior step onto a well-known folder. Take note of the full path. (I used c:\ffmpeg\)
5. Open the solution and set the <code>Unosquare.FFME.Windows.Sample</code> project as the startup project. You can do this by right clicking on the project and selecting <code>Set as startup project</code>
Expand Down
2 changes: 1 addition & 1 deletion Support/build-nuget-package.bat
@@ -1,6 +1,6 @@
@echo off
SET enableextensions
SET PackagePath="%UserProfile%\Desktop\ffme.windows-3.4.250\"
SET PackagePath="%UserProfile%\Desktop\ffme.windows-4.0.250\"
SET ProjectPath="C:\projects\ffmediaelement\"
SET ReleasePath="%ProjectPath%Unosquare.FFME.Windows.Sample\bin\Release\"

Expand Down
6 changes: 3 additions & 3 deletions Support/ffme.win.nuspec
Expand Up @@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>FFME.Windows</id>
<version>3.4.250</version>
<version>4.0.250</version>
<title>FFME: WPF MediaElement Control Alternative</title>
<authors>Mario Di Vece and the FFME contributors</authors>
<owners>MarioDiVece,Unosquare</owners>
Expand All @@ -14,7 +14,7 @@
<description>FFME is a close (and I'd like to think better) drop-in replacement for Microsoft's WPF MediaElement Control. While the standard MediaElement uses DirectX (DirectShow) for media playback, FFME uses FFmpeg to read and decode audio and video. This means that for those of you who want to support stuff like HLS playback, or just don't want to go through the hassle of installing codecs on client machines, using FFME might just be the answer.</description>
<summary>FFmpeg MediaElement Control (FFME)</summary>
<releaseNotes>
This is a release package of the Michelob build referencing bindings to FFmpeg version 3.4.2
This is a release package of the Michelob build referencing bindings to FFmpeg version 4.0
This package does not contain the required FFmpeg binaries. Please refer to the following URL for instructions on how to obtain the binaries: https://github.com/unosquare/ffmediaelement
Release details: https://github.com/unosquare/ffmediaelement/milestone/7?closed=1
</releaseNotes>
Expand All @@ -23,7 +23,7 @@
<tags>hls wpf ffmpeg mediaelement ffme h264 h265 hevc audio video processing decoding playback frame</tags>
<dependencies>
<group targetFramework=".NETFramework4.6.1">
<dependency id="FFmpeg.AutoGen" version="3.4.0.6" />
<dependency id="FFmpeg.AutoGen" version="4.0.0" />
</group>
</dependencies>
</metadata>
Expand Down
2 changes: 1 addition & 1 deletion Support/readme.txt
@@ -1,7 +1,7 @@
How to use FFME
In order to use the FFME MediaElement control, you will need to setup a folder with FFmpeg binaries. Here are the steps:

1. You can build your own FFmpeg or download a compatible build from the wonderful Zeranoe FFmpeg Builds site: (https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-3.4.2-win32-shared.zip).
1. You can build your own FFmpeg or download a compatible build from the wonderful Zeranoe FFmpeg Builds site: (https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-4.0-win32-shared.zip).
2. Your FFmpeg build (see the bin folder) should have 3 exe files and 8 dll files. Copy all 11 files to a folder such as (c:\ffmpeg)
3. Within you application's startup code (Main method), set Unosquare.FFME.MediaElement.FFmpegDirectory = "path to ffmpeg binaries from the previous step";.
4. Use the FFME MediaElement control as any other WPF control!
Expand Down
4 changes: 2 additions & 2 deletions Unosquare.FFME.Common/Commands/MediaCommand.cs
Expand Up @@ -49,7 +49,7 @@ protected MediaCommand(MediaCommandManager manager, MediaCommandType commandType
/// <summary>
/// Gets a value indicating whether this command is marked as completed.
/// </summary>
public bool HasCompleted => IsDisposed || TaskContext.IsCompleted;
public bool HasCompleted => IsDisposed || TaskContext == null || TaskContext.IsCompleted;

/// <summary>
/// Gets the task that this command will run.
Expand Down Expand Up @@ -93,7 +93,7 @@ public async Task StartAsync()
var m = Manager.MediaCore;

// Avoid processing the command if the element is disposed.
if (IsDisposed || m.IsDisposed || TaskContext.IsCanceled || IsRunning)
if (IsDisposed || m.IsDisposed || TaskContext.IsCanceled || IsRunning || TaskContext.IsCompleted)
return;

// Start and await the task
Expand Down
14 changes: 7 additions & 7 deletions Unosquare.FFME.Common/Commands/OpenCommand.cs
Expand Up @@ -66,7 +66,7 @@ internal override async Task ExecuteInternal()

// Create the stream container
// the async protocol prefix allows for increased performance for local files.
var streamOptions = new StreamOptions();
var containerConfig = new ContainerConfiguration();

// Convert the URI object to something the Media Container understands
var mediaUrl = Source.ToString();
Expand All @@ -76,7 +76,7 @@ internal override async Task ExecuteInternal()
{
// Set the default protocol Prefix
mediaUrl = Source.LocalPath;
streamOptions.ProtocolPrefix = "async";
containerConfig.ProtocolPrefix = "async";
}
}
catch { }
Expand All @@ -85,22 +85,22 @@ internal override async Task ExecuteInternal()
if (string.IsNullOrWhiteSpace(Source.Scheme) == false
&& (Source.Scheme.Equals("format") || Source.Scheme.Equals("device"))
&& string.IsNullOrWhiteSpace(Source.Host) == false
&& string.IsNullOrWhiteSpace(streamOptions.Input.ForcedInputFormat)
&& string.IsNullOrWhiteSpace(containerConfig.ForcedInputFormat)
&& string.IsNullOrWhiteSpace(Source.Query) == false)
{
// Update the Input format and container input URL
// It is also possible to set some input options as follows:
// streamOptions.Input.Add(StreamInputOptions.Names.FrameRate, "20");
streamOptions.Input.ForcedInputFormat = Source.Host;
// streamOptions.PrivateOptions["framerate"] = "20";
containerConfig.ForcedInputFormat = Source.Host;
mediaUrl = Uri.UnescapeDataString(Source.Query).TrimStart('?');
m.Log(MediaLogMessageType.Info, $"Media URI will be updated. Input Format: {Source.Host}, Input Argument: {mediaUrl}");
}

// Allow the stream input options to be changed
await m.SendOnMediaInitializing(streamOptions, mediaUrl);
await m.SendOnMediaInitializing(containerConfig, mediaUrl);

// Instantiate the internal container
m.Container = new MediaContainer(mediaUrl, streamOptions, m);
m.Container = new MediaContainer(mediaUrl, containerConfig, m);

// Notify the user media is opening and allow for media options to be modified
// Stuff like audio and video filters and stream selection can be performed here.
Expand Down
4 changes: 2 additions & 2 deletions Unosquare.FFME.Common/Core/FFAudioParams.cs
Expand Up @@ -53,8 +53,8 @@ private FFAudioParams()
/// <param name="frame">The frame.</param>
private FFAudioParams(AVFrame* frame)
{
ChannelCount = ffmpeg.av_frame_get_channels(frame);
ChannelLayout = ffmpeg.av_frame_get_channel_layout(frame);
ChannelCount = frame->channels;
ChannelLayout = unchecked((long)frame->channel_layout);
Format = (AVSampleFormat)frame->format;
SamplesPerChannel = frame->nb_samples;
BufferLength = ffmpeg.av_samples_get_buffer_size(null, ChannelCount, SamplesPerChannel, Format, 1);
Expand Down
128 changes: 122 additions & 6 deletions Unosquare.FFME.Common/Core/FFInterop.cs
Expand Up @@ -3,6 +3,7 @@
using FFmpeg.AutoGen;
using Shared;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
Expand Down Expand Up @@ -104,16 +105,15 @@ public static unsafe bool Initialize(string overridePath, int libIdentifiers)

// Additional library initialization
if (FFLibrary.LibAVDevice.IsLoaded) ffmpeg.avdevice_register_all();
if (FFLibrary.LibAVFilter.IsLoaded) ffmpeg.avfilter_register_all();

// Standard set initialization
ffmpeg.av_register_all();
ffmpeg.avcodec_register_all();
ffmpeg.avformat_network_init();
// Standard set initialization -- not needed anymore starting FFmpeg 4
// if (FFLibrary.LibAVFilter.IsLoaded) ffmpeg.avfilter_register_all();
// ffmpeg.av_register_all();
// ffmpeg.avcodec_register_all();
// ffmpeg.avformat_network_init();

// Logging and locking
LoggingWorker.ConnectToFFmpeg();
FFLockManager.Register();

// set the static environment properties
m_LibrariesPath = ffmpegPath;
Expand Down Expand Up @@ -187,6 +187,122 @@ public static unsafe string PtrToStringUTF8(byte* stringAddress)
}
}

/// <summary>
/// Retrieves the options information associated with the given AVClass.
/// </summary>
/// <param name="avClass">The av class.</param>
/// <returns>A list of option metadata</returns>
public static unsafe List<OptionMeta> RetrieveOptions(AVClass* avClass)
{
// see: https://github.com/FFmpeg/FFmpeg/blob/e0f32286861ddf7666ba92297686fa216d65968e/tools/enum_options.c
var result = new List<OptionMeta>(128);
if (avClass == null) return result;

AVOption* option = avClass->option;

while (option != null)
{
if (option->type != AVOptionType.AV_OPT_TYPE_CONST)
result.Add(new OptionMeta(option));

option = ffmpeg.av_opt_next(avClass, option);
}

return result;
}

/// <summary>
/// Retrives the codecs.
/// </summary>
/// <returns>The codecs</returns>
public static unsafe AVCodec*[] RetriveCodecs()
{
var result = new List<IntPtr>(1024);
void* iterator;
AVCodec* item;
while ((item = ffmpeg.av_codec_iterate(&iterator)) != null)
{
result.Add(new IntPtr(item));
}

var collection = new AVCodec*[result.Count];
for (var i = 0; i < result.Count; i++)
{
collection[i] = (AVCodec*)result[i].ToPointer();
}

return collection;
}

/// <summary>
/// Retrieves the input format names.
/// </summary>
/// <returns>The collection of names</returns>
public static unsafe List<string> RetrieveInputFormatNames()
{
var result = new List<string>(128);
void* iterator;
AVInputFormat* item;
while ((item = ffmpeg.av_demuxer_iterate(&iterator)) != null)
{
result.Add(PtrToStringUTF8(item->name));
}

return result;
}

/// <summary>
/// Retrieves the decoder names.
/// </summary>
/// <param name="allCodecs">All codecs.</param>
/// <returns>The collection of names</returns>
public static unsafe List<string> RetrieveDecoderNames(AVCodec*[] allCodecs)
{
var codecNames = new List<string>(allCodecs.Length);
foreach (var c in allCodecs)
{
if (ffmpeg.av_codec_is_decoder(c) != 0)
codecNames.Add(PtrToStringUTF8(c->name));
}

return codecNames;
}

/// <summary>
/// Retrieves the global format options.
/// </summary>
/// <returns>The collection of option infos</returns>
public static unsafe List<OptionMeta> RetrieveGlobalFormatOptions() =>
RetrieveOptions(ffmpeg.avformat_get_class());

/// <summary>
/// Retrieves the global codec options.
/// </summary>
/// <returns>The collection of option infos</returns>
public static unsafe List<OptionMeta> RetrieveGlobalCodecOptions() =>
RetrieveOptions(ffmpeg.avcodec_get_class());

/// <summary>
/// Retrieves the input format options.
/// </summary>
/// <param name="formatName">Name of the format.</param>
/// <returns>The collection of option infos</returns>
public static unsafe List<OptionMeta> RetrieveInputFormatOptions(string formatName)
{
var item = ffmpeg.av_find_input_format(formatName);
if (item == null) return new List<OptionMeta>(0);

return RetrieveOptions(item->priv_class);
}

/// <summary>
/// Retrieves the codec options.
/// </summary>
/// <param name="codec">The codec.</param>
/// <returns>The collection of option infos</returns>
public static unsafe List<OptionMeta> RetrieveCodecOptions(AVCodec* codec) =>
RetrieveOptions(codec->priv_class);

#endregion
}
}
16 changes: 8 additions & 8 deletions Unosquare.FFME.Common/Core/FFLibrary.cs
Expand Up @@ -64,37 +64,37 @@ private FFLibrary(string name, int version, int flagId)
/// <summary>
/// Gets the AVCodec library.
/// </summary>
public static FFLibrary LibAVCodec { get; } = new FFLibrary(Names.AVCodec, 57, 1);
public static FFLibrary LibAVCodec { get; } = new FFLibrary(Names.AVCodec, 58, 1);

/// <summary>
/// Gets the AVFormat library.
/// </summary>
public static FFLibrary LibAVFormat { get; } = new FFLibrary(Names.AVFormat, 57, 2);
public static FFLibrary LibAVFormat { get; } = new FFLibrary(Names.AVFormat, 58, 2);

/// <summary>
/// Gets the AVUtil library.
/// </summary>
public static FFLibrary LibAVUtil { get; } = new FFLibrary(Names.AVUtil, 55, 4);
public static FFLibrary LibAVUtil { get; } = new FFLibrary(Names.AVUtil, 56, 4);

/// <summary>
/// Gets the SWResample library.
/// </summary>
public static FFLibrary LibSWResample { get; } = new FFLibrary(Names.SWResample, 2, 8);
public static FFLibrary LibSWResample { get; } = new FFLibrary(Names.SWResample, 3, 8);

/// <summary>
/// Gets the SWScale library.
/// </summary>
public static FFLibrary LibSWScale { get; } = new FFLibrary(Names.SWScale, 4, 16);
public static FFLibrary LibSWScale { get; } = new FFLibrary(Names.SWScale, 5, 16);

/// <summary>
/// Gets the AVDevice library.
/// </summary>
public static FFLibrary LibAVDevice { get; } = new FFLibrary(Names.AVDevice, 57, 32);
public static FFLibrary LibAVDevice { get; } = new FFLibrary(Names.AVDevice, 58, 32);

/// <summary>
/// Gets the AVFilter library.
/// </summary>
public static FFLibrary LibAVFilter { get; } = new FFLibrary(Names.AVFilter, 6, 64);
public static FFLibrary LibAVFilter { get; } = new FFLibrary(Names.AVFilter, 7, 64);

#endregion

Expand Down Expand Up @@ -153,7 +153,7 @@ public bool Load(string basePath)
if (Reference != IntPtr.Zero)
throw new InvalidOperationException($"Library {Name} was already loaded.");

var result = LibraryLoader.LoadNativeLibraryUsingPlatformNamingConvention(basePath, Name, Version);
var result = LibraryLoader.LoadNativeLibrary(basePath, Name, Version);

if (result != IntPtr.Zero)
{
Expand Down

0 comments on commit fd78438

Please sign in to comment.