Update 'AVCustomEdit' sample (#327)
* added new project * fixed playing status * updated formatting * added several optimization steps * added cooperation with transition conrtoller * updated photo library saving * fixed export * fxied launch screen and transitions * fixed transitions * deleted old project * added assets * small optimization * added data * Update README.md * fixed iPhone X UI * Updated storyboard
|
@ -1,7 +1,7 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||
# Visual Studio 2010
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AVCustomEdit", "AVCustomEdit\AVCustomEdit.csproj", "{B086E3A5-1D26-4F87-B908-2521E3091A2D}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AVCustomEdit", "AVCustomEdit\AVCustomEdit.csproj", "{E35D29CE-B9F3-41D9-A22A-C00FD94D392C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -13,30 +13,27 @@ Global
|
|||
AppStore|iPhone = AppStore|iPhone
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{B086E3A5-1D26-4F87-B908-2521E3091A2D}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
|
||||
{B086E3A5-1D26-4F87-B908-2521E3091A2D}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
|
||||
{B086E3A5-1D26-4F87-B908-2521E3091A2D}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
|
||||
{B086E3A5-1D26-4F87-B908-2521E3091A2D}.AppStore|iPhone.Build.0 = AppStore|iPhone
|
||||
{B086E3A5-1D26-4F87-B908-2521E3091A2D}.Debug|iPhone.ActiveCfg = Debug|iPhone
|
||||
{B086E3A5-1D26-4F87-B908-2521E3091A2D}.Debug|iPhone.Build.0 = Debug|iPhone
|
||||
{B086E3A5-1D26-4F87-B908-2521E3091A2D}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
|
||||
{B086E3A5-1D26-4F87-B908-2521E3091A2D}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
|
||||
{B086E3A5-1D26-4F87-B908-2521E3091A2D}.Release|iPhone.ActiveCfg = Release|iPhone
|
||||
{B086E3A5-1D26-4F87-B908-2521E3091A2D}.Release|iPhone.Build.0 = Release|iPhone
|
||||
{B086E3A5-1D26-4F87-B908-2521E3091A2D}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
||||
{B086E3A5-1D26-4F87-B908-2521E3091A2D}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
||||
{E35D29CE-B9F3-41D9-A22A-C00FD94D392C}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
|
||||
{E35D29CE-B9F3-41D9-A22A-C00FD94D392C}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
|
||||
{E35D29CE-B9F3-41D9-A22A-C00FD94D392C}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
||||
{E35D29CE-B9F3-41D9-A22A-C00FD94D392C}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
||||
{E35D29CE-B9F3-41D9-A22A-C00FD94D392C}.Debug|iPhone.ActiveCfg = Debug|iPhone
|
||||
{E35D29CE-B9F3-41D9-A22A-C00FD94D392C}.Debug|iPhone.Build.0 = Debug|iPhone
|
||||
{E35D29CE-B9F3-41D9-A22A-C00FD94D392C}.Release|iPhone.ActiveCfg = Release|iPhone
|
||||
{E35D29CE-B9F3-41D9-A22A-C00FD94D392C}.Release|iPhone.Build.0 = Release|iPhone
|
||||
{E35D29CE-B9F3-41D9-A22A-C00FD94D392C}.Ad-Hoc|iPhone.ActiveCfg = Release|iPhone
|
||||
{E35D29CE-B9F3-41D9-A22A-C00FD94D392C}.Ad-Hoc|iPhone.Build.0 = Release|iPhone
|
||||
{E35D29CE-B9F3-41D9-A22A-C00FD94D392C}.AppStore|iPhone.ActiveCfg = Release|iPhone
|
||||
{E35D29CE-B9F3-41D9-A22A-C00FD94D392C}.AppStore|iPhone.Build.0 = Release|iPhone
|
||||
EndGlobalSection
|
||||
GlobalSection(MonoDevelopProperties) = preSolution
|
||||
StartupItem = AVCustomEdit\AVCustomEdit.csproj
|
||||
Policies = $0
|
||||
$0.TextStylePolicy = $1
|
||||
$1.TabsToSpaces = False
|
||||
$1.inheritsSet = VisualStudio
|
||||
$1.inheritsScope = text/plain
|
||||
$1.scope = text/x-csharp
|
||||
$1.FileWidth = 80
|
||||
$1.inheritsSet = null
|
||||
$0.CSharpFormattingPolicy = $2
|
||||
$2.inheritsSet = Mono
|
||||
$2.inheritsScope = text/x-csharp
|
||||
$2.scope = text/x-csharp
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
@ -1,149 +1,144 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{B086E3A5-1D26-4F87-B908-2521E3091A2D}</ProjectGuid>
|
||||
<ProjectTypeGuids>{FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>AVCustomEdit</RootNamespace>
|
||||
<IPhoneResourcePrefix>Resources</IPhoneResourcePrefix>
|
||||
<AssemblyName>AVCustomEdit</AssemblyName>
|
||||
<TargetFrameworkIdentifier>Xamarin.iOS</TargetFrameworkIdentifier>
|
||||
<TargetFrameworkVersion>v1.0</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
<DebugType></DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\iPhoneSimulator\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<MtouchLink>None</MtouchLink>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
<MtouchDebug>true</MtouchDebug>
|
||||
<MtouchArch>x86_64</MtouchArch>
|
||||
<MtouchHttpClientHandler>NSUrlSessionHandler</MtouchHttpClientHandler>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' ">
|
||||
<DebugType></DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\iPhoneSimulator\Release</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<MtouchLink>None</MtouchLink>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
<MtouchArch>x86_64</MtouchArch>
|
||||
<MtouchHttpClientHandler>NSUrlSessionHandler</MtouchHttpClientHandler>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' ">
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
<DebugType></DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\iPhone\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<MtouchDebug>true</MtouchDebug>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
<CodesignKey>iPhone Developer</CodesignKey>
|
||||
<MtouchArch>ARM64</MtouchArch>
|
||||
<MtouchUseRefCounting>true</MtouchUseRefCounting>
|
||||
<MtouchI18n></MtouchI18n>
|
||||
<MtouchHttpClientHandler>NSUrlSessionHandler</MtouchHttpClientHandler>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' ">
|
||||
<DebugType></DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\iPhone\Release</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<CodesignKey>iPhone Developer</CodesignKey>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
<MtouchArch>ARM64</MtouchArch>
|
||||
<MtouchHttpClientHandler>NSUrlSessionHandler</MtouchHttpClientHandler>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Ad-Hoc|iPhone' ">
|
||||
<DebugType></DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\iPhone\Ad-Hoc</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<CodesignKey>iPhone Distribution</CodesignKey>
|
||||
<BuildIpa>true</BuildIpa>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
<CodesignProvision>Automatic:AdHoc</CodesignProvision>
|
||||
<MtouchArch>ARM64</MtouchArch>
|
||||
<MtouchHttpClientHandler>NSUrlSessionHandler</MtouchHttpClientHandler>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'AppStore|iPhone' ">
|
||||
<DebugType></DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\iPhone\AppStore</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<CodesignKey>iPhone Distribution</CodesignKey>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
<CodesignProvision>Automatic:AppStore</CodesignProvision>
|
||||
<MtouchArch>ARM64</MtouchArch>
|
||||
<MtouchHttpClientHandler>NSUrlSessionHandler</MtouchHttpClientHandler>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="Xamarin.iOS" />
|
||||
<Reference Include="OpenTK-1.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Resources\" />
|
||||
<Folder Include="OpenGL Renderers\" />
|
||||
<Folder Include="Custom Compositors\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Info.plist" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Main.cs" />
|
||||
<Compile Include="AppDelegate.cs" />
|
||||
<Compile Include="PlayerViewController.cs" />
|
||||
<Compile Include="PlayerViewController.designer.cs">
|
||||
<DependentUpon>PlayerViewController.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="PlayerView.cs" />
|
||||
<Compile Include="PlayerView.designer.cs">
|
||||
<DependentUpon>PlayerView.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="TransitionTypeController.cs" />
|
||||
<Compile Include="TransitionTypeController.designer.cs">
|
||||
<DependentUpon>TransitionTypeController.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SimpleEditor.cs" />
|
||||
<Compile Include="OpenGL Renderers\DiagonalWipeRenderer.cs" />
|
||||
<Compile Include="OpenGL Renderers\CrossDissolveRenderer.cs" />
|
||||
<Compile Include="OpenGL Renderers\OpenGLRenderer.cs" />
|
||||
<Compile Include="Custom Compositors\CustomVideoCompositionInstruction.cs" />
|
||||
<Compile Include="Custom Compositors\CustomVideoCompositor.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<InterfaceDefinition Include="MainStoryboard.storyboard" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BundleResource Include="sample_clip1.m4v" />
|
||||
<BundleResource Include="sample_clip2.mov" />
|
||||
<BundleResource Include="Resources\Icon.png" />
|
||||
<BundleResource Include="Resources\Icon%402x.png" />
|
||||
<BundleResource Include="Resources\Icon-72.png" />
|
||||
<BundleResource Include="Resources\Icon-72%402x.png" />
|
||||
<BundleResource Include="Resources\Icon-Small.png" />
|
||||
<BundleResource Include="Resources\Icon-Small%402x.png" />
|
||||
<BundleResource Include="Resources\Icon-Small-50.png" />
|
||||
<BundleResource Include="Resources\Default.png" />
|
||||
<BundleResource Include="Resources\Default%402x.png" />
|
||||
<BundleResource Include="Resources\Default-568h%402x.png" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{E35D29CE-B9F3-41D9-A22A-C00FD94D392C}</ProjectGuid>
|
||||
<ProjectTypeGuids>{FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>AVCustomEdit</RootNamespace>
|
||||
<AssemblyName>AVCustomEdit</AssemblyName>
|
||||
<IPhoneResourcePrefix>Resources</IPhoneResourcePrefix>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\iPhoneSimulator\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;ENABLE_TEST_CLOUD;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<CodesignKey>iPhone Developer</CodesignKey>
|
||||
<MtouchDebug>true</MtouchDebug>
|
||||
<MtouchNoSymbolStrip>true</MtouchNoSymbolStrip>
|
||||
<MtouchFastDev>true</MtouchFastDev>
|
||||
<IOSDebuggerPort>13612</IOSDebuggerPort>
|
||||
<MtouchLink>None</MtouchLink>
|
||||
<MtouchArch>x86_64</MtouchArch>
|
||||
<MtouchHttpClientHandler>NSUrlSessionHandler</MtouchHttpClientHandler>
|
||||
<DeviceSpecificBuild>false</DeviceSpecificBuild>
|
||||
<MtouchVerbosity></MtouchVerbosity>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\iPhone\Release</OutputPath>
|
||||
<DefineConstants></DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<CodesignKey>iPhone Developer</CodesignKey>
|
||||
<MtouchUseLlvm>true</MtouchUseLlvm>
|
||||
<MtouchFloat32>true</MtouchFloat32>
|
||||
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
|
||||
<MtouchLink>SdkOnly</MtouchLink>
|
||||
<MtouchArch>ARM64</MtouchArch>
|
||||
<MtouchHttpClientHandler>NSUrlSessionHandler</MtouchHttpClientHandler>
|
||||
<MtouchVerbosity></MtouchVerbosity>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\iPhoneSimulator\Release</OutputPath>
|
||||
<DefineConstants></DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<CodesignKey>iPhone Developer</CodesignKey>
|
||||
<MtouchNoSymbolStrip>true</MtouchNoSymbolStrip>
|
||||
<MtouchLink>None</MtouchLink>
|
||||
<MtouchArch>x86_64</MtouchArch>
|
||||
<MtouchHttpClientHandler>NSUrlSessionHandler</MtouchHttpClientHandler>
|
||||
<MtouchVerbosity></MtouchVerbosity>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\iPhone\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;ENABLE_TEST_CLOUD;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<CodesignKey>iPhone Developer</CodesignKey>
|
||||
<DeviceSpecificBuild>true</DeviceSpecificBuild>
|
||||
<MtouchDebug>true</MtouchDebug>
|
||||
<MtouchNoSymbolStrip>true</MtouchNoSymbolStrip>
|
||||
<MtouchFastDev>true</MtouchFastDev>
|
||||
<MtouchFloat32>true</MtouchFloat32>
|
||||
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
|
||||
<IOSDebuggerPort>11505</IOSDebuggerPort>
|
||||
<MtouchLink>SdkOnly</MtouchLink>
|
||||
<MtouchArch>ARM64</MtouchArch>
|
||||
<MtouchHttpClientHandler>NSUrlSessionHandler</MtouchHttpClientHandler>
|
||||
<MtouchVerbosity></MtouchVerbosity>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="Xamarin.iOS" />
|
||||
<Reference Include="OpenTK-1.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Contents.json" />
|
||||
<ImageAsset Include="Assets.xcassets\Contents.json" />
|
||||
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\app-store-logo.png" />
|
||||
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-app-83.5%402x.png" />
|
||||
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\icon-app-76%402x.png" />
|
||||
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\icon-app-76.png" />
|
||||
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\icon-spotlight-29.png" />
|
||||
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\icon-spotlight-29%402x.png" />
|
||||
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\icon-spotlight-29%403x.png" />
|
||||
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\icon-app-60%402x.png" />
|
||||
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-app-60%403x.png" />
|
||||
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\icon-spotlight-40%402x.png" />
|
||||
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\icon-spotlight-40%403x.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Resources\" />
|
||||
<Folder Include="OpenGL Renderers\" />
|
||||
<Folder Include="Custom Compositors\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<InterfaceDefinition Include="LaunchScreen.storyboard" />
|
||||
<InterfaceDefinition Include="Main.storyboard" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Info.plist" />
|
||||
<None Include="Entitlements.plist" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Main.cs" />
|
||||
<Compile Include="AppDelegate.cs" />
|
||||
<Compile Include="ViewController.cs" />
|
||||
<Compile Include="ViewController.designer.cs">
|
||||
<DependentUpon>ViewController.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="PlayerView.cs" />
|
||||
<Compile Include="SimpleEditor.cs" />
|
||||
<Compile Include="TransitionTypeController.cs" />
|
||||
<Compile Include="TransitionTypeController.designer.cs">
|
||||
<DependentUpon>TransitionTypeController.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="OpenGL Renderers\CrossDissolveRenderer.cs" />
|
||||
<Compile Include="OpenGL Renderers\DiagonalWipeRenderer.cs" />
|
||||
<Compile Include="OpenGL Renderers\OpenGLRenderer.cs" />
|
||||
<Compile Include="Custom Compositors\CustomVideoCompositionInstruction.cs" />
|
||||
<Compile Include="Custom Compositors\CustomVideoCompositor.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BundleResource Include="Resources\sample_clip1.m4v" />
|
||||
<BundleResource Include="Resources\sample_clip2.mov" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
|
||||
</Project>
|
|
@ -1,21 +1,21 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Foundation;
|
||||
using Foundation;
|
||||
using UIKit;
|
||||
|
||||
namespace AVCustomEdit
|
||||
{
|
||||
// The UIApplicationDelegate for the application. This class is responsible for launching the
|
||||
// User Interface of the application, as well as listening (and optionally responding) to
|
||||
// application events from iOS.
|
||||
// User Interface of the application, as well as listening (and optionally responding) to application events from iOS.
|
||||
[Register ("AppDelegate")]
|
||||
public partial class AppDelegate : UIApplicationDelegate
|
||||
public class AppDelegate : UIApplicationDelegate
|
||||
{
|
||||
// class-level declarations
|
||||
public override UIWindow Window {
|
||||
get;
|
||||
set;
|
||||
public override UIWindow Window { get; set; }
|
||||
|
||||
public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
|
||||
{
|
||||
// Override point for customization after application launch.
|
||||
// If not required for your application you can safely delete this method
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,244 @@
|
|||
{
|
||||
"images": [
|
||||
{
|
||||
"size": "20x20",
|
||||
"scale": "2x",
|
||||
"idiom": "iphone"
|
||||
},
|
||||
{
|
||||
"size": "20x20",
|
||||
"scale": "3x",
|
||||
"idiom": "iphone"
|
||||
},
|
||||
{
|
||||
"filename": "icon-spotlight-29@2x.png",
|
||||
"size": "29x29",
|
||||
"scale": "2x",
|
||||
"idiom": "iphone"
|
||||
},
|
||||
{
|
||||
"filename": "icon-spotlight-29@3x.png",
|
||||
"size": "29x29",
|
||||
"scale": "3x",
|
||||
"idiom": "iphone"
|
||||
},
|
||||
{
|
||||
"filename": "icon-spotlight-40@2x.png",
|
||||
"size": "40x40",
|
||||
"scale": "2x",
|
||||
"idiom": "iphone"
|
||||
},
|
||||
{
|
||||
"filename": "icon-spotlight-40@3x.png",
|
||||
"size": "40x40",
|
||||
"scale": "3x",
|
||||
"idiom": "iphone"
|
||||
},
|
||||
{
|
||||
"filename": "icon-app-60@2x.png",
|
||||
"size": "60x60",
|
||||
"scale": "2x",
|
||||
"idiom": "iphone"
|
||||
},
|
||||
{
|
||||
"filename": "Icon-app-60@3x.png",
|
||||
"size": "60x60",
|
||||
"scale": "3x",
|
||||
"idiom": "iphone"
|
||||
},
|
||||
{
|
||||
"size": "20x20",
|
||||
"scale": "1x",
|
||||
"idiom": "ipad"
|
||||
},
|
||||
{
|
||||
"size": "20x20",
|
||||
"scale": "2x",
|
||||
"idiom": "ipad"
|
||||
},
|
||||
{
|
||||
"filename": "icon-spotlight-29.png",
|
||||
"size": "29x29",
|
||||
"scale": "1x",
|
||||
"idiom": "ipad"
|
||||
},
|
||||
{
|
||||
"filename": "icon-spotlight-29@2x.png",
|
||||
"size": "29x29",
|
||||
"scale": "2x",
|
||||
"idiom": "ipad"
|
||||
},
|
||||
{
|
||||
"size": "40x40",
|
||||
"scale": "1x",
|
||||
"idiom": "ipad"
|
||||
},
|
||||
{
|
||||
"filename": "icon-spotlight-40@2x.png",
|
||||
"size": "40x40",
|
||||
"scale": "2x",
|
||||
"idiom": "ipad"
|
||||
},
|
||||
{
|
||||
"filename": "Icon-app-83.5@2x.png",
|
||||
"size": "83.5x83.5",
|
||||
"scale": "2x",
|
||||
"idiom": "ipad"
|
||||
},
|
||||
{
|
||||
"filename": "icon-app-76.png",
|
||||
"size": "76x76",
|
||||
"scale": "1x",
|
||||
"idiom": "ipad"
|
||||
},
|
||||
{
|
||||
"filename": "icon-app-76@2x.png",
|
||||
"size": "76x76",
|
||||
"scale": "2x",
|
||||
"idiom": "ipad"
|
||||
},
|
||||
{
|
||||
"filename": "app-store-logo.png",
|
||||
"size": "1024x1024",
|
||||
"scale": "1x",
|
||||
"idiom": "ios-marketing"
|
||||
},
|
||||
{
|
||||
"size": "60x60",
|
||||
"scale": "2x",
|
||||
"idiom": "car"
|
||||
},
|
||||
{
|
||||
"size": "60x60",
|
||||
"scale": "3x",
|
||||
"idiom": "car"
|
||||
},
|
||||
{
|
||||
"role": "notificationCenter",
|
||||
"size": "24x24",
|
||||
"subtype": "38mm",
|
||||
"scale": "2x",
|
||||
"idiom": "watch"
|
||||
},
|
||||
{
|
||||
"role": "notificationCenter",
|
||||
"size": "27.5x27.5",
|
||||
"subtype": "42mm",
|
||||
"scale": "2x",
|
||||
"idiom": "watch"
|
||||
},
|
||||
{
|
||||
"role": "companionSettings",
|
||||
"size": "29x29",
|
||||
"scale": "2x",
|
||||
"idiom": "watch"
|
||||
},
|
||||
{
|
||||
"role": "companionSettings",
|
||||
"size": "29x29",
|
||||
"scale": "3x",
|
||||
"idiom": "watch"
|
||||
},
|
||||
{
|
||||
"role": "appLauncher",
|
||||
"size": "40x40",
|
||||
"subtype": "38mm",
|
||||
"scale": "2x",
|
||||
"idiom": "watch"
|
||||
},
|
||||
{
|
||||
"role": "appLauncher",
|
||||
"size": "44x44",
|
||||
"subtype": "40mm",
|
||||
"scale": "2x",
|
||||
"idiom": "watch"
|
||||
},
|
||||
{
|
||||
"role": "appLauncher",
|
||||
"size": "50x50",
|
||||
"subtype": "44mm",
|
||||
"scale": "2x",
|
||||
"idiom": "watch"
|
||||
},
|
||||
{
|
||||
"role": "quickLook",
|
||||
"size": "86x86",
|
||||
"subtype": "38mm",
|
||||
"scale": "2x",
|
||||
"idiom": "watch"
|
||||
},
|
||||
{
|
||||
"role": "quickLook",
|
||||
"size": "98x98",
|
||||
"subtype": "42mm",
|
||||
"scale": "2x",
|
||||
"idiom": "watch"
|
||||
},
|
||||
{
|
||||
"role": "quickLook",
|
||||
"size": "108x108",
|
||||
"subtype": "44mm",
|
||||
"scale": "2x",
|
||||
"idiom": "watch"
|
||||
},
|
||||
{
|
||||
"size": "1024x1024",
|
||||
"scale": "1x",
|
||||
"idiom": "watch-marketing"
|
||||
},
|
||||
{
|
||||
"size": "16x16",
|
||||
"scale": "1x",
|
||||
"idiom": "mac"
|
||||
},
|
||||
{
|
||||
"size": "16x16",
|
||||
"scale": "2x",
|
||||
"idiom": "mac"
|
||||
},
|
||||
{
|
||||
"size": "32x32",
|
||||
"scale": "1x",
|
||||
"idiom": "mac"
|
||||
},
|
||||
{
|
||||
"size": "32x32",
|
||||
"scale": "2x",
|
||||
"idiom": "mac"
|
||||
},
|
||||
{
|
||||
"size": "128x128",
|
||||
"scale": "1x",
|
||||
"idiom": "mac"
|
||||
},
|
||||
{
|
||||
"size": "128x128",
|
||||
"scale": "2x",
|
||||
"idiom": "mac"
|
||||
},
|
||||
{
|
||||
"size": "256x256",
|
||||
"scale": "1x",
|
||||
"idiom": "mac"
|
||||
},
|
||||
{
|
||||
"size": "256x256",
|
||||
"scale": "2x",
|
||||
"idiom": "mac"
|
||||
},
|
||||
{
|
||||
"size": "512x512",
|
||||
"scale": "1x",
|
||||
"idiom": "mac"
|
||||
},
|
||||
{
|
||||
"size": "512x512",
|
||||
"scale": "2x",
|
||||
"idiom": "mac"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"version": 1,
|
||||
"author": "xcode"
|
||||
}
|
||||
}
|
Двоичные данные
AVCustomEdit/AVCustomEdit/Assets.xcassets/AppIcon.appiconset/Icon-app-60@3x.png
Normal file
После Ширина: | Высота: | Размер: 5.0 KiB |
Двоичные данные
AVCustomEdit/AVCustomEdit/Assets.xcassets/AppIcon.appiconset/Icon-app-83.5@2x.png
Normal file
После Ширина: | Высота: | Размер: 8.6 KiB |
Двоичные данные
AVCustomEdit/AVCustomEdit/Assets.xcassets/AppIcon.appiconset/app-store-logo.png
Normal file
После Ширина: | Высота: | Размер: 22 KiB |
Двоичные данные
AVCustomEdit/AVCustomEdit/Assets.xcassets/AppIcon.appiconset/icon-app-60@2x.png
Normal file
После Ширина: | Высота: | Размер: 3.3 KiB |
Двоичные данные
AVCustomEdit/AVCustomEdit/Assets.xcassets/AppIcon.appiconset/icon-app-76.png
Normal file
После Ширина: | Высота: | Размер: 2.0 KiB |
Двоичные данные
AVCustomEdit/AVCustomEdit/Assets.xcassets/AppIcon.appiconset/icon-app-76@2x.png
Normal file
После Ширина: | Высота: | Размер: 4.3 KiB |
Двоичные данные
AVCustomEdit/AVCustomEdit/Assets.xcassets/AppIcon.appiconset/icon-spotlight-29.png
Normal file
После Ширина: | Высота: | Размер: 900 B |
Двоичные данные
AVCustomEdit/AVCustomEdit/Assets.xcassets/AppIcon.appiconset/icon-spotlight-29@2x.png
Normal file
После Ширина: | Высота: | Размер: 1.8 KiB |
Двоичные данные
AVCustomEdit/AVCustomEdit/Assets.xcassets/AppIcon.appiconset/icon-spotlight-29@3x.png
Normal file
После Ширина: | Высота: | Размер: 2.5 KiB |
Двоичные данные
AVCustomEdit/AVCustomEdit/Assets.xcassets/AppIcon.appiconset/icon-spotlight-40@2x.png
Normal file
После Ширина: | Высота: | Размер: 2.2 KiB |
Двоичные данные
AVCustomEdit/AVCustomEdit/Assets.xcassets/AppIcon.appiconset/icon-spotlight-40@3x.png
Normal file
После Ширина: | Высота: | Размер: 3.3 KiB |
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
|
@ -1,68 +1,80 @@
|
|||
using AVFoundation;
|
||||
using CoreMedia;
|
||||
using Foundation;
|
||||
using ObjCRuntime;
|
||||
|
||||
namespace AVCustomEdit
|
||||
{
|
||||
public class CustomVideoCompositionInstruction : AVVideoCompositionInstruction
|
||||
{
|
||||
public int ForegroundTrackID;
|
||||
public int BackgroundTrackID;
|
||||
public class CustomVideoCompositionInstruction : AVVideoCompositionInstruction
|
||||
{
|
||||
private readonly NSNumber[] requiredSourceTrackIds;
|
||||
private readonly bool enablePostProcessing;
|
||||
private readonly int passthroughTrackId;
|
||||
private readonly bool containsTweening;
|
||||
private readonly CMTimeRange timeRange;
|
||||
|
||||
CMTimeRange timeRange;
|
||||
readonly int passthroughTrackID;
|
||||
readonly NSNumber [] requiredSourceTrackIDs;
|
||||
readonly bool enablePostProcessing;
|
||||
readonly bool containsTweening;
|
||||
public CustomVideoCompositionInstruction() : base() { }
|
||||
|
||||
public override int PassthroughTrackID {
|
||||
get {
|
||||
return passthroughTrackID;
|
||||
}
|
||||
}
|
||||
public CustomVideoCompositionInstruction(int passthroughTrackId, CMTimeRange timeRange) : base()
|
||||
{
|
||||
this.passthroughTrackId = passthroughTrackId;
|
||||
this.requiredSourceTrackIds = null;
|
||||
this.timeRange = timeRange;
|
||||
this.containsTweening = false;
|
||||
this.enablePostProcessing = false;
|
||||
}
|
||||
|
||||
public override NSNumber [] RequiredSourceTrackIDs {
|
||||
get {
|
||||
return requiredSourceTrackIDs;
|
||||
}
|
||||
}
|
||||
public CustomVideoCompositionInstruction(NSNumber[] sourceTracksIds, CMTimeRange timeRange) : base()
|
||||
{
|
||||
this.requiredSourceTrackIds = sourceTracksIds;
|
||||
this.passthroughTrackId = 0;
|
||||
this.timeRange = timeRange;
|
||||
this.containsTweening = true;
|
||||
this.enablePostProcessing = false;
|
||||
}
|
||||
|
||||
public override CMTimeRange TimeRange {
|
||||
get {
|
||||
return timeRange;
|
||||
}
|
||||
}
|
||||
public override bool EnablePostProcessing {
|
||||
get {
|
||||
return enablePostProcessing;
|
||||
}
|
||||
}
|
||||
public override bool ContainsTweening {
|
||||
get {
|
||||
return containsTweening;
|
||||
}
|
||||
}
|
||||
public int ForegroundTrackId { get; set; }
|
||||
|
||||
public CustomVideoCompositionInstruction ()
|
||||
{
|
||||
}
|
||||
public int BackgroundTrackId { get; set; }
|
||||
|
||||
public CustomVideoCompositionInstruction (int passthroughTrackID, CMTimeRange timeRange)
|
||||
{
|
||||
this.passthroughTrackID = passthroughTrackID;
|
||||
requiredSourceTrackIDs = null;
|
||||
this.timeRange = timeRange;
|
||||
containsTweening = false;
|
||||
enablePostProcessing = false;
|
||||
}
|
||||
public override int PassthroughTrackID => this.passthroughTrackId;
|
||||
|
||||
public CustomVideoCompositionInstruction(NSNumber [] sourceTracksIDS, CMTimeRange timeRange)
|
||||
{
|
||||
requiredSourceTrackIDs = sourceTracksIDS;
|
||||
passthroughTrackID = 0;
|
||||
this.timeRange = timeRange;
|
||||
containsTweening = true;
|
||||
enablePostProcessing = false;
|
||||
}
|
||||
}
|
||||
public override NSNumber[] RequiredSourceTrackIDs => this.requiredSourceTrackIds;
|
||||
|
||||
public override CMTimeRange TimeRange => this.timeRange;
|
||||
|
||||
public override bool EnablePostProcessing => this.enablePostProcessing;
|
||||
|
||||
public override bool ContainsTweening => this.containsTweening;
|
||||
|
||||
[return: Release]
|
||||
public override NSObject Copy()
|
||||
{
|
||||
return CustomVideoCompositionInstruction.Copy(this);
|
||||
}
|
||||
|
||||
[return: Release]
|
||||
public override NSObject MutableCopy()
|
||||
{
|
||||
return CustomVideoCompositionInstruction.Copy(this);
|
||||
}
|
||||
|
||||
public static CustomVideoCompositionInstruction Copy(CustomVideoCompositionInstruction current)
|
||||
{
|
||||
CustomVideoCompositionInstruction result = null;
|
||||
if (current.RequiredSourceTrackIDs != null && current.RequiredSourceTrackIDs.Length > 0)
|
||||
{
|
||||
result = new CustomVideoCompositionInstruction(current.RequiredSourceTrackIDs, current.TimeRange);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = new CustomVideoCompositionInstruction(current.PassthroughTrackID, current.TimeRange);
|
||||
}
|
||||
|
||||
result.ForegroundTrackId = current.ForegroundTrackId;
|
||||
result.BackgroundTrackId = current.BackgroundTrackId;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,149 +1,174 @@
|
|||
using System;
|
||||
using CoreGraphics;
|
||||
|
||||
using Foundation;
|
||||
using AVFoundation;
|
||||
using CoreFoundation;
|
||||
using CoreVideo;
|
||||
using CoreGraphics;
|
||||
using CoreMedia;
|
||||
using CoreVideo;
|
||||
using Foundation;
|
||||
using System;
|
||||
|
||||
namespace AVCustomEdit
|
||||
{
|
||||
public class CrossDissolveCompositor : CustomVideoCompositor
|
||||
{
|
||||
public CrossDissolveCompositor (IntPtr handle) : base (handle)
|
||||
{
|
||||
oglRender = new CrossDissolveRenderer ();
|
||||
}
|
||||
public class CrossDissolveCompositor : CustomVideoCompositor
|
||||
{
|
||||
public CrossDissolveCompositor(IntPtr handle) : base(handle)
|
||||
{
|
||||
Render = new CrossDissolveRenderer();
|
||||
}
|
||||
|
||||
public CrossDissolveCompositor(AVVideoComposition videoComposition) : base(videoComposition)
|
||||
{
|
||||
oglRender = new CrossDissolveRenderer ();
|
||||
}
|
||||
}
|
||||
public CrossDissolveCompositor(AVVideoComposition videoComposition) : base(videoComposition)
|
||||
{
|
||||
Render = new CrossDissolveRenderer();
|
||||
}
|
||||
}
|
||||
|
||||
public class DiagonalWipeCompositor : CustomVideoCompositor
|
||||
{
|
||||
public DiagonalWipeCompositor (IntPtr handle) : base (handle)
|
||||
{
|
||||
oglRender = new DiagonalWipeRenderer ();
|
||||
}
|
||||
public class DiagonalWipeCompositor : CustomVideoCompositor
|
||||
{
|
||||
public DiagonalWipeCompositor(IntPtr handle) : base(handle)
|
||||
{
|
||||
Render = new DiagonalWipeRenderer();
|
||||
}
|
||||
|
||||
public DiagonalWipeCompositor(AVVideoComposition videoComposition) : base(videoComposition)
|
||||
{
|
||||
oglRender = new DiagonalWipeRenderer ();
|
||||
}
|
||||
}
|
||||
public DiagonalWipeCompositor(AVVideoComposition videoComposition) : base(videoComposition)
|
||||
{
|
||||
Render = new DiagonalWipeRenderer();
|
||||
}
|
||||
}
|
||||
|
||||
public class CustomVideoCompositor : AVVideoCompositing
|
||||
{
|
||||
bool shouldCancelAllRequests;
|
||||
bool renderContextDidChange;
|
||||
DispatchQueue renderingQueue;
|
||||
DispatchQueue renderContextQueue;
|
||||
AVVideoCompositionRenderContext renderContext;
|
||||
//CVPixelBuffer previousBuffer;
|
||||
public OpenGLRenderer oglRender;
|
||||
public class CustomVideoCompositor : AVVideoCompositing
|
||||
{
|
||||
private bool shouldCancelAllRequests;
|
||||
private bool renderContextDidChange;
|
||||
private DispatchQueue renderingQueue;
|
||||
private DispatchQueue renderContextQueue;
|
||||
private AVVideoCompositionRenderContext renderContext;
|
||||
//private CVPixelBuffer previousBuffer;
|
||||
|
||||
public CustomVideoCompositor (IntPtr handle) : base (handle)
|
||||
{
|
||||
renderingQueue = new DispatchQueue ("com.apple.aplcustomvideocompositor.renderingqueue");
|
||||
renderContextQueue = new DispatchQueue ("com.apple.aplcustomvideocompositor.rendercontextqueue");
|
||||
renderContextDidChange = false;
|
||||
}
|
||||
public CustomVideoCompositor(IntPtr handle) : base(handle)
|
||||
{
|
||||
renderingQueue = new DispatchQueue("com.apple.aplcustomvideocompositor.renderingqueue");
|
||||
renderContextQueue = new DispatchQueue("com.apple.aplcustomvideocompositor.rendercontextqueue");
|
||||
//previousBuffer = null;
|
||||
renderContextDidChange = false;
|
||||
}
|
||||
|
||||
public CustomVideoCompositor (AVVideoComposition videoComposition)
|
||||
{
|
||||
renderingQueue = new DispatchQueue ("com.apple.aplcustomvideocompositor.renderingqueue");
|
||||
renderContextQueue = new DispatchQueue ("com.apple.aplcustomvideocompositor.rendercontextqueue");
|
||||
renderContextDidChange = false;
|
||||
}
|
||||
public CustomVideoCompositor(AVVideoComposition videoComposition)
|
||||
{
|
||||
renderingQueue = new DispatchQueue("com.apple.aplcustomvideocompositor.renderingqueue");
|
||||
renderContextQueue = new DispatchQueue("com.apple.aplcustomvideocompositor.rendercontextqueue");
|
||||
//previousBuffer = null;
|
||||
renderContextDidChange = false;
|
||||
}
|
||||
|
||||
public override NSDictionary SourcePixelBufferAttributes ()
|
||||
{
|
||||
return new NSDictionary (CVPixelBuffer.PixelFormatTypeKey, CVPixelFormatType.CV420YpCbCr8BiPlanarVideoRange,
|
||||
CVPixelBuffer.OpenGLESCompatibilityKey, true);
|
||||
}
|
||||
public OpenGLRenderer Render { get; set; }
|
||||
|
||||
public override NSDictionary RequiredPixelBufferAttributesForRenderContext ()
|
||||
{
|
||||
return new NSDictionary (CVPixelBuffer.PixelFormatTypeKey, CVPixelFormatType.CV420YpCbCr8BiPlanarVideoRange,
|
||||
CVPixelBuffer.OpenGLESCompatibilityKey, true);
|
||||
}
|
||||
public override NSDictionary SourcePixelBufferAttributes()
|
||||
{
|
||||
return new NSDictionary(CVPixelBuffer.PixelFormatTypeKey, CVPixelFormatType.CV420YpCbCr8BiPlanarVideoRange,
|
||||
CVPixelBuffer.OpenGLESCompatibilityKey, true);
|
||||
}
|
||||
|
||||
public override void RenderContextChanged (AVVideoCompositionRenderContext newRenderContext)
|
||||
{
|
||||
renderContextQueue.DispatchSync (() => {
|
||||
renderContext = newRenderContext;
|
||||
renderContextDidChange = true;
|
||||
});
|
||||
}
|
||||
public override NSDictionary RequiredPixelBufferAttributesForRenderContext()
|
||||
{
|
||||
return new NSDictionary(CVPixelBuffer.PixelFormatTypeKey, CVPixelFormatType.CV420YpCbCr8BiPlanarVideoRange,
|
||||
CVPixelBuffer.OpenGLESCompatibilityKey, true);
|
||||
}
|
||||
|
||||
public override void StartVideoCompositionRequest (AVAsynchronousVideoCompositionRequest asyncVideoCompositionRequest)
|
||||
{
|
||||
renderingQueue.DispatchAsync (() => {
|
||||
if(shouldCancelAllRequests)
|
||||
asyncVideoCompositionRequest.FinishCancelledRequest();
|
||||
else
|
||||
{
|
||||
NSError error;
|
||||
CVPixelBuffer resultPixels = newRenderedPixelBufferForRequest( asyncVideoCompositionRequest, out error);
|
||||
if(resultPixels != null){
|
||||
asyncVideoCompositionRequest.FinishWithComposedVideoFrame(resultPixels);
|
||||
resultPixels.Dispose();
|
||||
}
|
||||
else{
|
||||
asyncVideoCompositionRequest.FinishWithError(error);
|
||||
}
|
||||
public override void RenderContextChanged(AVVideoCompositionRenderContext newRenderContext)
|
||||
{
|
||||
renderContextQueue.DispatchSync(() =>
|
||||
{
|
||||
renderContext = newRenderContext;
|
||||
renderContextDidChange = true;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
public override void StartVideoCompositionRequest(AVAsynchronousVideoCompositionRequest request)
|
||||
{
|
||||
renderingQueue.DispatchAsync(() =>
|
||||
{
|
||||
// Check if all pending requests have been cancelled
|
||||
if (shouldCancelAllRequests)
|
||||
{
|
||||
request.FinishCancelledRequest();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the next rendererd pixel buffer
|
||||
var resultPixels = NewRenderedPixelBufferForRequest(request, out NSError error);
|
||||
if (resultPixels != null)
|
||||
{
|
||||
// The resulting pixelbuffer from OpenGL renderer is passed along to the request
|
||||
request.FinishWithComposedVideoFrame(resultPixels);
|
||||
resultPixels.Dispose();
|
||||
resultPixels = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
request.FinishWithError(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public override void CancelAllPendingVideoCompositionRequests ()
|
||||
{
|
||||
shouldCancelAllRequests = true;
|
||||
renderingQueue.DispatchAsync (() => {
|
||||
shouldCancelAllRequests = false;
|
||||
});
|
||||
}
|
||||
public override void CancelAllPendingVideoCompositionRequests()
|
||||
{
|
||||
// pending requests will call finishCancelledRequest, those already rendering will call finishWithComposedVideoFrame
|
||||
shouldCancelAllRequests = true;
|
||||
|
||||
//Utilities methods
|
||||
renderingQueue.DispatchAsync(() =>
|
||||
{
|
||||
// start accepting requests again
|
||||
shouldCancelAllRequests = false;
|
||||
});
|
||||
}
|
||||
|
||||
static double FactorForTimeInRange( CMTime time, CMTimeRange range)
|
||||
{
|
||||
CMTime elapsed = CMTime.Subtract (time, range.Start);
|
||||
return elapsed.Seconds / range.Duration.Seconds;
|
||||
}
|
||||
#region Utilities methods
|
||||
|
||||
CVPixelBuffer newRenderedPixelBufferForRequest (AVAsynchronousVideoCompositionRequest request, out NSError error )
|
||||
{
|
||||
CVPixelBuffer dstPixels;
|
||||
float tweenFactor =(float) FactorForTimeInRange (request.CompositionTime, request.VideoCompositionInstruction.TimeRange);
|
||||
private static double FactorForTimeInRange(CMTime time, CMTimeRange range)
|
||||
{
|
||||
var elapsed = CMTime.Subtract(time, range.Start);
|
||||
return elapsed.Seconds / range.Duration.Seconds;
|
||||
}
|
||||
|
||||
var currentInstruction = (CustomVideoCompositionInstruction)request.VideoCompositionInstruction;
|
||||
private CVPixelBuffer NewRenderedPixelBufferForRequest(AVAsynchronousVideoCompositionRequest request, out NSError error)
|
||||
{
|
||||
CVPixelBuffer dstPixels;
|
||||
|
||||
CVPixelBuffer foregroundSourceBuffer = request.SourceFrameByTrackID (currentInstruction.ForegroundTrackID);
|
||||
CVPixelBuffer backgroundSourceBuffer = request.SourceFrameByTrackID (currentInstruction.BackgroundTrackID);
|
||||
// tweenFactor indicates how far within that timeRange are we rendering this frame. This is normalized to vary between 0.0 and 1.0.
|
||||
// 0.0 indicates the time at first frame in that videoComposition timeRange
|
||||
// 1.0 indicates the time at last frame in that videoComposition timeRange
|
||||
var tweenFactor = (float)FactorForTimeInRange(request.CompositionTime, request.VideoCompositionInstruction.TimeRange);
|
||||
|
||||
dstPixels = renderContext.CreatePixelBuffer ();
|
||||
var currentInstruction = request.VideoCompositionInstruction as CustomVideoCompositionInstruction;
|
||||
|
||||
if (renderContextDidChange) {
|
||||
var renderSize = renderContext.Size;
|
||||
var destinationSize = new CGSize (dstPixels.Width, dstPixels.Height);
|
||||
var renderContextTransform = new CGAffineTransform (renderSize.Width / 2, 0, 0, renderSize.Height / 2, renderSize.Width / 2, renderSize.Height / 2);
|
||||
var destinationTransform = new CGAffineTransform (2 / destinationSize.Width, 0, 0, 2 / destinationSize.Height, -1, -1);
|
||||
var normalizedRenderTransform = CGAffineTransform.Multiply( CGAffineTransform.Multiply(renderContextTransform, renderContext.RenderTransform), destinationTransform);
|
||||
oglRender.RenderTransform = normalizedRenderTransform;
|
||||
// Source pixel buffers are used as inputs while rendering the transition
|
||||
var foregroundSourceBuffer = request.SourceFrameByTrackID(currentInstruction.ForegroundTrackId);
|
||||
var backgroundSourceBuffer = request.SourceFrameByTrackID(currentInstruction.BackgroundTrackId);
|
||||
|
||||
renderContextDidChange = false;
|
||||
}
|
||||
// Destination pixel buffer into which we render the output
|
||||
dstPixels = renderContext.CreatePixelBuffer();
|
||||
|
||||
oglRender.RenderPixelBuffer (dstPixels, foregroundSourceBuffer, backgroundSourceBuffer, tweenFactor);
|
||||
// Recompute normalized render transform everytime the render context changes
|
||||
if (renderContextDidChange)
|
||||
{
|
||||
// The renderTransform returned by the renderContext is in X: [0, w] and Y: [0, h] coordinate system
|
||||
// But since in this sample we render using OpenGLES which has its coordinate system between [-1, 1] we compute a normalized transform
|
||||
var renderSize = renderContext.Size;
|
||||
var destinationSize = new CGSize(dstPixels.Width, dstPixels.Height);
|
||||
var renderContextTransform = new CGAffineTransform(renderSize.Width / 2, 0, 0, renderSize.Height / 2, renderSize.Width / 2, renderSize.Height / 2);
|
||||
var destinationTransform = new CGAffineTransform(2 / destinationSize.Width, 0, 0, 2 / destinationSize.Height, -1, -1);
|
||||
var normalizedRenderTransform = CGAffineTransform.Multiply(CGAffineTransform.Multiply(renderContextTransform, renderContext.RenderTransform), destinationTransform);
|
||||
Render.RenderTransform = normalizedRenderTransform;
|
||||
|
||||
error = null;
|
||||
return dstPixels;
|
||||
}
|
||||
}
|
||||
}
|
||||
renderContextDidChange = false;
|
||||
}
|
||||
|
||||
Render.RenderPixelBuffer(dstPixels, foregroundSourceBuffer, backgroundSourceBuffer, tweenFactor);
|
||||
|
||||
error = null;
|
||||
return dstPixels;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,42 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>MainStoryboard</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>9.0</string>
|
||||
<key>UIDeviceFamily</key>
|
||||
<array>
|
||||
<integer>1</integer>
|
||||
<integer>2</integer>
|
||||
</array>
|
||||
<key>UIStatusBarTintParameters</key>
|
||||
<dict>
|
||||
<key>UINavigationBar</key>
|
||||
<dict>
|
||||
<key>Style</key>
|
||||
<string>UIBarStyleDefault</string>
|
||||
<key>Translucent</key>
|
||||
<false />
|
||||
</dict>
|
||||
</dict>
|
||||
<key>CFBundleIconFiles</key>
|
||||
<array>
|
||||
<string>Icon</string>
|
||||
<string>Icon@2x</string>
|
||||
<string>Icon-72</string>
|
||||
<string>Icon-72@2x</string>
|
||||
<string>Icon-Small</string>
|
||||
<string>Icon-Small@2x</string>
|
||||
<string>Icon-Small-50</string>
|
||||
</array>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<key>CFBundleName</key>
|
||||
<string>AVCustomEdit</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.xamarin.AVCustomEdit</string>
|
||||
|
@ -44,11 +10,30 @@
|
|||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>9.0</string>
|
||||
<key>UIDeviceFamily</key>
|
||||
<array>
|
||||
<integer>1</integer>
|
||||
<integer>2</integer>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>XSAppIconAssets</key>
|
||||
<string>Assets.xcassets/AppIcon.appiconset</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Access to PhotoLibrary for export feature</string>
|
||||
</dict>
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Xamarin" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0EM-Jt-5LB">
|
||||
<rect key="frame" x="116.5" y="319.5" width="142" height="48"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="40"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="0EM-Jt-5LB" firstAttribute="centerY" secondItem="LzO-nd-BUG" secondAttribute="centerY" id="TYB-Rs-GJa"/>
|
||||
<constraint firstItem="0EM-Jt-5LB" firstAttribute="centerX" secondItem="LzO-nd-BUG" secondAttribute="centerX" id="qAU-C5-ays"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="LzO-nd-BUG"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
|
@ -1,19 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Foundation;
|
||||
using UIKit;
|
||||
using UIKit;
|
||||
|
||||
namespace AVCustomEdit
|
||||
{
|
||||
public class Application
|
||||
{
|
||||
// This is the main entry point of the application.
|
||||
static void Main (string[] args)
|
||||
static void Main (string [] args)
|
||||
{
|
||||
// if you want to use a different Application Delegate class from "AppDelegate"
|
||||
// you can specify it here.
|
||||
UIApplication.Main (args, null, "AppDelegate");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,202 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||
<device id="retina4_7" orientation="landscape">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="667" height="375"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="LD4-Le-jcl" customClass="PlayerView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="667" height="375"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<connections>
|
||||
<outletCollection property="gestureRecognizers" destination="sLC-Cu-nWY" appends="YES" id="HWH-G8-9PM"/>
|
||||
</connections>
|
||||
</view>
|
||||
<progressView hidden="YES" opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kEb-nT-KfT">
|
||||
<rect key="frame" x="258.5" y="323" width="150" height="2"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="150" id="boE-uP-EQB"/>
|
||||
</constraints>
|
||||
</progressView>
|
||||
<toolbar opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" barStyle="black" translatesAutoresizingMaskIntoConstraints="NO" id="4sS-5v-XkA">
|
||||
<rect key="frame" x="0.0" y="341" width="667" height="34"/>
|
||||
<items>
|
||||
<barButtonItem style="plain" systemItem="play" id="EYI-u1-A8G">
|
||||
<connections>
|
||||
<action selector="TogglePlayPause:" destination="BYZ-38-t0r" id="N3Z-8b-dQc"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem style="plain" id="A6v-IJ-sMa">
|
||||
<slider key="customView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" id="fHe-9A-hM0">
|
||||
<rect key="frame" x="37" y="0.0" width="204" height="34"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<connections>
|
||||
<action selector="BeginScrubbing:" destination="BYZ-38-t0r" eventType="touchDown" id="eHY-jB-UQ4"/>
|
||||
<action selector="EndScrubbing:" destination="BYZ-38-t0r" eventType="touchUpInside" id="AyU-3Z-Leu"/>
|
||||
<action selector="EndScrubbing:" destination="BYZ-38-t0r" eventType="touchUpOutside" id="HVd-Gp-Qpg"/>
|
||||
<action selector="EndScrubbing:" destination="BYZ-38-t0r" eventType="touchCancel" id="jEC-H0-ztW"/>
|
||||
<action selector="Scrub:" destination="BYZ-38-t0r" eventType="valueChanged" id="RXh-oW-bvG"/>
|
||||
</connections>
|
||||
</slider>
|
||||
</barButtonItem>
|
||||
<barButtonItem style="plain" systemItem="flexibleSpace" id="Qbw-sK-BJN"/>
|
||||
<barButtonItem title="Set Transition" id="6uz-rN-HPP">
|
||||
<connections>
|
||||
<segue destination="R9e-Pk-PpT" kind="presentation" identifier="Transition" id="1354"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem title="Export" id="xIp-Oh-YCB">
|
||||
<connections>
|
||||
<action selector="ExportToMovie:" destination="BYZ-38-t0r" id="ebu-5Y-QnB"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</items>
|
||||
</toolbar>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="ddd" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="iyX-Gk-aIe">
|
||||
<rect key="frame" x="256" y="342" width="48" height="21"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="21" id="49U-gc-cgh"/>
|
||||
<constraint firstAttribute="width" constant="48" id="Xsh-ZO-Iie"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="LD4-Le-jcl" secondAttribute="trailing" id="JcL-ai-hsJ"/>
|
||||
<constraint firstItem="iyX-Gk-aIe" firstAttribute="leading" secondItem="lKW-hZ-kwj" secondAttribute="leading" constant="256" id="KvA-GL-IGT"/>
|
||||
<constraint firstItem="LD4-Le-jcl" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leading" id="LGn-eA-lFf"/>
|
||||
<constraint firstItem="4sS-5v-XkA" firstAttribute="trailing" secondItem="8bC-Xf-vdC" secondAttribute="trailing" id="MJG-q2-xEK"/>
|
||||
<constraint firstItem="4sS-5v-XkA" firstAttribute="centerX" secondItem="kEb-nT-KfT" secondAttribute="centerX" id="cRw-Hw-r9y"/>
|
||||
<constraint firstItem="iyX-Gk-aIe" firstAttribute="bottom" secondItem="4sS-5v-XkA" secondAttribute="bottom" constant="-12" id="dbf-pq-r04"/>
|
||||
<constraint firstAttribute="bottom" secondItem="LD4-Le-jcl" secondAttribute="bottom" id="hfk-51-is5"/>
|
||||
<constraint firstAttribute="leading" secondItem="4sS-5v-XkA" secondAttribute="leading" symbolic="YES" id="iWh-sU-oDY"/>
|
||||
<constraint firstItem="LD4-Le-jcl" firstAttribute="top" secondItem="8bC-Xf-vdC" secondAttribute="top" id="isS-W9-DiJ"/>
|
||||
<constraint firstItem="4sS-5v-XkA" firstAttribute="top" secondItem="kEb-nT-KfT" secondAttribute="bottom" constant="16" id="m2G-PQ-rNK"/>
|
||||
<constraint firstAttribute="bottom" secondItem="4sS-5v-XkA" secondAttribute="bottom" id="pN1-vs-Azc"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="lKW-hZ-kwj"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="currentTimeLabel" destination="iyX-Gk-aIe" id="name-outlet-iyX-Gk-aIe"/>
|
||||
<outlet property="exportButton" destination="xIp-Oh-YCB" id="name-outlet-xIp-Oh-YCB"/>
|
||||
<outlet property="exportProgressView" destination="kEb-nT-KfT" id="name-outlet-kEb-nT-KfT"/>
|
||||
<outlet property="playPauseButton" destination="EYI-u1-A8G" id="name-outlet-EYI-u1-A8G"/>
|
||||
<outlet property="playerView" destination="LD4-Le-jcl" id="name-outlet-LD4-Le-jcl"/>
|
||||
<outlet property="scrubber" destination="fHe-9A-hM0" id="name-outlet-fHe-9A-hM0"/>
|
||||
<outlet property="toolbar" destination="4sS-5v-XkA" id="name-outlet-4sS-5v-XkA"/>
|
||||
<outlet property="transitionButton" destination="6uz-rN-HPP" id="name-outlet-6uz-rN-HPP"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
<tapGestureRecognizer id="sLC-Cu-nWY">
|
||||
<connections>
|
||||
<action selector="HandleTapGesture:" destination="BYZ-38-t0r" id="407"/>
|
||||
<outlet property="delegate" destination="BYZ-38-t0r" id="e8g-ul-YGd"/>
|
||||
</connections>
|
||||
</tapGestureRecognizer>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="0.0" y="0.0"/>
|
||||
</scene>
|
||||
<!--Navigation Controller-->
|
||||
<scene sceneID="oF8-X1-4jb">
|
||||
<objects>
|
||||
<navigationController definesPresentationContext="YES" id="R9e-Pk-PpT" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="BNi-gR-eHR">
|
||||
<rect key="frame" x="0.0" y="0.0" width="667" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
<segue destination="1IP-es-DBQ" kind="relationship" relationship="rootViewController" id="hMp-g1-JcV"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="kbS-mz-Eti" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="702" y="-574"/>
|
||||
</scene>
|
||||
<!--Set Transition-->
|
||||
<scene sceneID="X1A-oB-eDW">
|
||||
<objects>
|
||||
<tableViewController id="1IP-es-DBQ" customClass="TransitionTypeController" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="44" estimatedRowHeight="-1" sectionHeaderHeight="10" sectionFooterHeight="10" id="FbG-9a-Ewx">
|
||||
<rect key="frame" x="0.0" y="0.0" width="667" height="375"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||
<sections>
|
||||
<tableViewSection headerTitle="Transition Type" id="1042">
|
||||
<cells>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" indentationLevel="1" indentationWidth="0.0" textLabel="nno-KP-PYO" style="IBUITableViewCellStyleDefault" id="1043">
|
||||
<rect key="frame" x="0.0" y="55.5" width="667" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="1043" id="1044">
|
||||
<rect key="frame" x="0.0" y="0.0" width="667" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Diagonal Wipe" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="nno-KP-PYO">
|
||||
<rect key="frame" x="20" y="0.0" width="627" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="blue" indentationLevel="1" indentationWidth="0.0" textLabel="7iK-G1-2ez" style="IBUITableViewCellStyleDefault" id="1045">
|
||||
<rect key="frame" x="0.0" y="99.5" width="667" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="1045" id="1046">
|
||||
<rect key="frame" x="0.0" y="0.0" width="667" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Cross Dissolve" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="7iK-G1-2ez">
|
||||
<rect key="frame" x="20" y="0.0" width="627" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="1IP-es-DBQ" id="5Zm-ji-Kq2"/>
|
||||
<outlet property="delegate" destination="1IP-es-DBQ" id="Yt3-gX-f2e"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="Set Transition" id="H2T-Lk-SG8">
|
||||
<barButtonItem key="rightBarButtonItem" title="Done" style="done" id="2f7-fq-iCZ">
|
||||
<connections>
|
||||
<action selector="TransitionSelected:" destination="1IP-es-DBQ" id="772"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="crossDissolveCell" destination="1045" id="name-outlet-1045"/>
|
||||
<outlet property="diagonalWipeCell" destination="1043" id="name-outlet-1043"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="aMV-Ds-sup" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="701" y="289"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
|
@ -1,186 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="5053" systemVersion="13C64" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" initialViewController="64">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="3733"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Player View Controller-->
|
||||
<scene sceneID="63">
|
||||
<objects>
|
||||
<viewController id="64" customClass="PlayerViewController" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="65">
|
||||
<rect key="frame" x="0.0" y="0.0" width="568" height="320"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<toolbar opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" barStyle="blackTranslucent" id="dMF-K1-Sws">
|
||||
<rect key="frame" x="-3" y="276" width="570" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<color key="backgroundColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<items>
|
||||
<barButtonItem systemItem="play" id="HOB-PP-3UB">
|
||||
<connections>
|
||||
<action selector="togglePlayPause:" destination="64" id="370"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem style="plain" id="9Zj-cl-VbD">
|
||||
<slider key="customView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" id="oEO-gT-r2P">
|
||||
<rect key="frame" x="42" y="5" width="168" height="34"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<connections>
|
||||
<action selector="beginScrubbing:" destination="64" eventType="touchDown" id="365"/>
|
||||
<action selector="endScrubbing:" destination="64" eventType="touchCancel" id="366"/>
|
||||
<action selector="endScrubbing:" destination="64" eventType="touchUpInside" id="367"/>
|
||||
<action selector="endScrubbing:" destination="64" eventType="touchUpOutside" id="368"/>
|
||||
<action selector="scrub:" destination="64" eventType="valueChanged" id="369"/>
|
||||
</connections>
|
||||
</slider>
|
||||
</barButtonItem>
|
||||
<barButtonItem style="plain" systemItem="flexibleSpace" id="68"/>
|
||||
<barButtonItem title="Transition" id="70">
|
||||
<connections>
|
||||
<segue destination="90" kind="modal" identifier="Transition" id="FXO-CA-qDp"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem title="Export" id="75">
|
||||
<connections>
|
||||
<action selector="exportToMovie:" destination="64" id="374"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem style="plain" id="240"/>
|
||||
</items>
|
||||
</toolbar>
|
||||
<view contentMode="scaleToFill" id="83" customClass="PlayerView">
|
||||
<rect key="frame" x="-1" y="20" width="569" height="255"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<progressView hidden="YES" opaque="NO" contentMode="scaleToFill" progress="0.5" id="361">
|
||||
<rect key="frame" x="166" y="247" width="150" height="2"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
</progressView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||
</view>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="U69-c1-S53">
|
||||
<rect key="frame" x="221" y="287" width="62" height="23"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<simulatedOrientationMetrics key="simulatedOrientationMetrics" orientation="landscapeRight"/>
|
||||
</view>
|
||||
<extendedEdge key="edgesForExtendedLayout"/>
|
||||
<simulatedOrientationMetrics key="simulatedOrientationMetrics" orientation="landscapeRight"/>
|
||||
<connections>
|
||||
<outlet property="currentTimeLabel" destination="U69-c1-S53" id="name-outlet-U69-c1-S53"/>
|
||||
<outlet property="exportButton" destination="75" id="name-outlet-75"/>
|
||||
<outlet property="exportProgressView" destination="361" id="name-outlet-361"/>
|
||||
<outlet property="gestureRecognizer" destination="373" id="name-outlet-373"/>
|
||||
<outlet property="playPauseButton" destination="HOB-PP-3UB" id="name-outlet-HOB-PP-3UB"/>
|
||||
<outlet property="playerView" destination="83" id="s8j-Pv-pQ1"/>
|
||||
<outlet property="scrubber" destination="oEO-gT-r2P" id="name-outlet-oEO-gT-r2P"/>
|
||||
<outlet property="toolBar" destination="dMF-K1-Sws" id="i53-z0-8ml"/>
|
||||
<outlet property="transitionButton" destination="70" id="name-outlet-70"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="66" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
<tapGestureRecognizer id="373"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-914" y="-408"/>
|
||||
</scene>
|
||||
<!--Navigation Controller-->
|
||||
<scene sceneID="89">
|
||||
<objects>
|
||||
<navigationController definesPresentationContext="YES" id="90" sceneMemberID="viewController">
|
||||
<extendedEdge key="edgesForExtendedLayout"/>
|
||||
<simulatedOrientationMetrics key="simulatedOrientationMetrics" orientation="landscapeRight"/>
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" id="93">
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
<segue destination="294" kind="relationship" relationship="rootViewController" id="310"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="94" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-560" y="160"/>
|
||||
</scene>
|
||||
<!--Transition Type Controller - Transition Type-->
|
||||
<scene sceneID="293">
|
||||
<objects>
|
||||
<tableViewController id="294" customClass="TransitionTypeController" sceneMemberID="viewController">
|
||||
<tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="295">
|
||||
<rect key="frame" x="0.0" y="64" width="568" height="256"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<simulatedOrientationMetrics key="simulatedOrientationMetrics" orientation="landscapeRight"/>
|
||||
<sections>
|
||||
<tableViewSection headerTitle="Section-1" id="321">
|
||||
<cells>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" indentationWidth="10" textLabel="340" style="IBUITableViewCellStyleDefault" id="322">
|
||||
<rect key="frame" x="0.0" y="22" width="568" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="322" id="Pnh-ey-78t">
|
||||
<rect key="frame" x="0.0" y="0.0" width="568" height="43"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="Diagonal Wipe" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="340">
|
||||
<rect key="frame" x="15" y="0.0" width="538" height="43"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="20"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell tag="1" contentMode="scaleToFill" selectionStyle="blue" indentationWidth="10" textLabel="356" style="IBUITableViewCellStyleDefault" id="323">
|
||||
<rect key="frame" x="0.0" y="66" width="568" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="323" id="A5s-Uj-1yJ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="568" height="43"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" tag="1" contentMode="left" text="Cross Dissolve" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="356">
|
||||
<rect key="frame" x="15" y="0.0" width="538" height="43"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="20"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="294" id="296"/>
|
||||
<outlet property="delegate" destination="294" id="297"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<extendedEdge key="edgesForExtendedLayout"/>
|
||||
<navigationItem key="navigationItem" title="Transition Type" id="309">
|
||||
<barButtonItem key="rightBarButtonItem" title="Done" style="done" id="378">
|
||||
<connections>
|
||||
<action selector="TransitionSelected:" destination="294" id="gBS-xF-lRK"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
<simulatedOrientationMetrics key="simulatedOrientationMetrics" orientation="landscapeRight"/>
|
||||
<connections>
|
||||
<outlet property="crossDissolveCell" destination="323" id="name-outlet-323"/>
|
||||
<outlet property="diagonalWipeCell" destination="322" id="name-outlet-322"/>
|
||||
<outlet property="tableView" destination="295" id="name-outlet-295"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="299" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-87" y="-321"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<simulatedMetricsContainer key="defaultSimulatedMetrics">
|
||||
<simulatedStatusBarMetrics key="statusBar"/>
|
||||
<simulatedOrientationMetrics key="orientation"/>
|
||||
<simulatedScreenMetrics key="destination" type="retina4"/>
|
||||
</simulatedMetricsContainer>
|
||||
</document>
|
|
@ -1,204 +1,190 @@
|
|||
using System;
|
||||
using Foundation;
|
||||
using OpenGLES;
|
||||
using CoreGraphics;
|
||||
using CoreVideo;
|
||||
using OpenGLES;
|
||||
using OpenTK.Graphics.ES20;
|
||||
using System;
|
||||
|
||||
namespace AVCustomEdit
|
||||
{
|
||||
public class CrossDissolveRenderer : OpenGLRenderer
|
||||
{
|
||||
public CrossDissolveRenderer () : base()
|
||||
{
|
||||
}
|
||||
public class CrossDissolveRenderer : OpenGLRenderer
|
||||
{
|
||||
public override void RenderPixelBuffer(CoreVideo.CVPixelBuffer destinationPixelBuffer, CoreVideo.CVPixelBuffer foregroundPixelBuffer, CoreVideo.CVPixelBuffer backgroundPixelBuffer, float tween)
|
||||
{
|
||||
EAGLContext.SetCurrentContext(CurrentContext);
|
||||
if (foregroundPixelBuffer != null || backgroundPixelBuffer != null)
|
||||
{
|
||||
var foregroundLumaTexture = LumaTextureForPixelBuffer(foregroundPixelBuffer);
|
||||
var foregroundChromaTexture = ChromaTextureForPixelBuffer(foregroundPixelBuffer);
|
||||
|
||||
public override void RenderPixelBuffer (CoreVideo.CVPixelBuffer destinationPixelBuffer, CoreVideo.CVPixelBuffer foregroundPixelBuffer, CoreVideo.CVPixelBuffer backgroundPixelBuffer, float tween)
|
||||
{
|
||||
EAGLContext.SetCurrentContext (CurrentContext);
|
||||
if (foregroundPixelBuffer != null || backgroundPixelBuffer != null) {
|
||||
CVOpenGLESTexture foregroundLumaTexture = LumaTextureForPixelBuffer (foregroundPixelBuffer);
|
||||
CVOpenGLESTexture foregroundChromaTexture = ChromaTextureForPixelBuffer (foregroundPixelBuffer);
|
||||
var backgroundLumaTexture = LumaTextureForPixelBuffer(backgroundPixelBuffer);
|
||||
var backgroundChromaTexture = ChromaTextureForPixelBuffer(backgroundPixelBuffer);
|
||||
|
||||
CVOpenGLESTexture backgroundLumaTexture = LumaTextureForPixelBuffer (backgroundPixelBuffer);
|
||||
CVOpenGLESTexture backgroundChromaTexture = ChromaTextureForPixelBuffer (backgroundPixelBuffer);
|
||||
var destLumaTexture = LumaTextureForPixelBuffer(destinationPixelBuffer);
|
||||
var destChromaTexture = ChromaTextureForPixelBuffer(destinationPixelBuffer);
|
||||
|
||||
CVOpenGLESTexture destLumaTexture = LumaTextureForPixelBuffer (destinationPixelBuffer);
|
||||
CVOpenGLESTexture destChromaTexture = ChromaTextureForPixelBuffer (destinationPixelBuffer);
|
||||
GL.UseProgram(ProgramY);
|
||||
|
||||
GL.UseProgram (ProgramY);
|
||||
// Set the render transform
|
||||
float[] preferredRenderTransform =
|
||||
{
|
||||
(float)RenderTransform.xx, (float)RenderTransform.xy, (float)RenderTransform.x0, 0.0f,
|
||||
(float)RenderTransform.yx, (float)RenderTransform.yy, (float)RenderTransform.y0, 0.0f,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f,
|
||||
};
|
||||
|
||||
// Set the render transform
|
||||
float[] preferredRenderTransform = {
|
||||
(float)RenderTransform.xx, (float)RenderTransform.xy, (float)RenderTransform.x0, 0.0f,
|
||||
(float)RenderTransform.yx, (float)RenderTransform.yy,(float)RenderTransform.y0, 0.0f,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f,
|
||||
};
|
||||
GL.UniformMatrix4(Uniforms[(int)Uniform.Render_Transform_Y], 1, false, preferredRenderTransform);
|
||||
|
||||
GL.UniformMatrix4 (Uniforms [(int)Uniform.Render_Transform_Y], 1, false, preferredRenderTransform);
|
||||
GL.BindFramebuffer(FramebufferTarget.Framebuffer, OffscreenBufferHandle);
|
||||
GL.Viewport(0, 0, (int)destinationPixelBuffer.GetWidthOfPlane(0), (int)destinationPixelBuffer.GetHeightOfPlane(0));
|
||||
|
||||
GL.BindFramebuffer (FramebufferTarget.Framebuffer, OffscreenBufferHandle);
|
||||
GL.Viewport (0, 0, (int)destinationPixelBuffer.Width, (int)destinationPixelBuffer.Height);
|
||||
// Y planes of foreground and background frame are used to render the Y plane of the destination frame
|
||||
GL.ActiveTexture(TextureUnit.Texture0);
|
||||
GL.BindTexture(foregroundLumaTexture.Target, foregroundLumaTexture.Name);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)All.ClampToEdge);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)All.ClampToEdge);
|
||||
|
||||
GL.ActiveTexture (TextureUnit.Texture0);
|
||||
GL.BindTexture (foregroundLumaTexture.Target, foregroundLumaTexture.Name);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)All.ClampToEdge);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)All.ClampToEdge);
|
||||
GL.ActiveTexture(TextureUnit.Texture1);
|
||||
GL.BindTexture(backgroundLumaTexture.Target, backgroundLumaTexture.Name);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)All.ClampToEdge);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)All.ClampToEdge);
|
||||
|
||||
GL.ActiveTexture (TextureUnit.Texture1);
|
||||
GL.BindTexture (backgroundLumaTexture.Target, backgroundLumaTexture.Name);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)All.ClampToEdge);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)All.ClampToEdge);
|
||||
// Attach the destination texture as a color attachment to the off screen frame buffer
|
||||
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferSlot.ColorAttachment0, destLumaTexture.Target, destLumaTexture.Name, 0);
|
||||
|
||||
// Attach the destination texture as a color attachment to the off screen frame buffer
|
||||
GL.FramebufferTexture2D (FramebufferTarget.Framebuffer, FramebufferSlot.ColorAttachment0, destLumaTexture.Target, destLumaTexture.Name, 0);
|
||||
if (GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer) != FramebufferErrorCode.FramebufferComplete)
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to make complete framebuffer object {GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer)}");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (GL.CheckFramebufferStatus (FramebufferTarget.Framebuffer) != FramebufferErrorCode.FramebufferComplete) {
|
||||
Console.Error.WriteLine ("Failed to make complete framebuffer object " + GL.CheckFramebufferStatus (FramebufferTarget.Framebuffer));
|
||||
foregroundLumaTexture.Dispose ();
|
||||
foregroundChromaTexture.Dispose ();
|
||||
backgroundLumaTexture.Dispose ();
|
||||
backgroundChromaTexture.Dispose ();
|
||||
destLumaTexture.Dispose ();
|
||||
destChromaTexture.Dispose ();
|
||||
VideoTextureCache.Flush (0);
|
||||
EAGLContext.SetCurrentContext (null);
|
||||
return;
|
||||
}
|
||||
GL.ClearColor(0f, 0f, 0f, 1f);
|
||||
GL.Clear(ClearBufferMask.ColorBufferBit);
|
||||
|
||||
GL.ClearColor (0.0f, 0.0f, 0.0f, 1.0f);
|
||||
GL.Clear (ClearBufferMask.ColorBufferBit);
|
||||
float[] quadVertexData1 =
|
||||
{
|
||||
-1.0f, 1.0f,
|
||||
1.0f, 1.0f,
|
||||
-1.0f, -1.0f,
|
||||
1.0f, -1.0f,
|
||||
};
|
||||
|
||||
float[] quadVertexData1 = {
|
||||
-1.0f, 1.0f,
|
||||
1.0f, 1.0f,
|
||||
-1.0f, -1.0f,
|
||||
1.0f, -1.0f,
|
||||
};
|
||||
// texture data varies from 0 -> 1, whereas vertex data varies from -1 -> 1
|
||||
float[] quadTextureData1 = {
|
||||
0.5f + quadVertexData1 [0] / 2, 0.5f + quadVertexData1 [1] / 2,
|
||||
0.5f + quadVertexData1 [2] / 2, 0.5f + quadVertexData1 [3] / 2,
|
||||
0.5f + quadVertexData1 [4] / 2, 0.5f + quadVertexData1 [5] / 2,
|
||||
0.5f + quadVertexData1 [6] / 2, 0.5f + quadVertexData1 [7] / 2,
|
||||
};
|
||||
// texture data varies from 0 -> 1, whereas vertex data varies from -1 -> 1
|
||||
float[] quadTextureData1 =
|
||||
{
|
||||
0.5f + quadVertexData1[0] / 2, 0.5f + quadVertexData1[1] / 2,
|
||||
0.5f + quadVertexData1[2] / 2, 0.5f + quadVertexData1[3] / 2,
|
||||
0.5f + quadVertexData1[4] / 2, 0.5f + quadVertexData1[5] / 2,
|
||||
0.5f + quadVertexData1[6] / 2, 0.5f + quadVertexData1[7] / 2,
|
||||
};
|
||||
|
||||
GL.Uniform1 (Uniforms [(int)Uniform.Y], 0);
|
||||
GL.Uniform1(Uniforms[(int)Uniform.Y], 0);
|
||||
|
||||
GL.VertexAttribPointer ((int)Attrib.Vertex_Y, 2, VertexAttribPointerType.Float, false, 0, quadVertexData1);
|
||||
GL.EnableVertexAttribArray ((int)Attrib.Vertex_Y);
|
||||
GL.VertexAttribPointer((int)Attrib.Vertex_Y, 2, VertexAttribPointerType.Float, false, 0, quadVertexData1);
|
||||
GL.EnableVertexAttribArray((int)Attrib.Vertex_Y);
|
||||
|
||||
GL.VertexAttribPointer ((int)Attrib.TexCoord_Y, 2, VertexAttribPointerType.Float, false, 0, quadTextureData1);
|
||||
GL.EnableVertexAttribArray ((int)Attrib.TexCoord_Y);
|
||||
GL.VertexAttribPointer((int)Attrib.TexCoord_Y, 2, VertexAttribPointerType.Float, false, 0, quadTextureData1);
|
||||
GL.EnableVertexAttribArray((int)Attrib.TexCoord_Y);
|
||||
|
||||
// Blend function to draw the foreground frame
|
||||
GL.Enable (EnableCap.Blend);
|
||||
GL.BlendFunc (BlendingFactorSrc.One, BlendingFactorDest.Zero);
|
||||
// Blend function to draw the foreground frame
|
||||
GL.Enable(EnableCap.Blend);
|
||||
GL.BlendFunc(BlendingFactorSrc.One, BlendingFactorDest.Zero);
|
||||
|
||||
// Draw the foreground frame
|
||||
GL.DrawArrays (BeginMode.TriangleStrip, 0, 4);
|
||||
// Draw the foreground frame
|
||||
GL.DrawArrays(BeginMode.TriangleStrip, 0, 4);
|
||||
|
||||
GL.Uniform1 (Uniforms [(int)Uniform.Y], 1);
|
||||
GL.Uniform1(Uniforms[(int)Uniform.Y], 1);
|
||||
|
||||
GL.VertexAttribPointer ((int)Attrib.Vertex_Y, 2, VertexAttribPointerType.Float, false, 0, quadVertexData1);
|
||||
GL.EnableVertexAttribArray ((int)Attrib.Vertex_Y);
|
||||
GL.VertexAttribPointer((int)Attrib.Vertex_Y, 2, VertexAttribPointerType.Float, false, 0, quadVertexData1);
|
||||
GL.EnableVertexAttribArray((int)Attrib.Vertex_Y);
|
||||
|
||||
GL.VertexAttribPointer ((int)Attrib.TexCoord_Y, 2, VertexAttribPointerType.Float, false, 0, quadTextureData1);
|
||||
GL.EnableVertexAttribArray ((int)Attrib.TexCoord_Y);
|
||||
GL.VertexAttribPointer((int)Attrib.TexCoord_Y, 2, VertexAttribPointerType.Float, false, 0, quadTextureData1);
|
||||
GL.EnableVertexAttribArray((int)Attrib.TexCoord_Y);
|
||||
|
||||
// Blend function to draw the background frame
|
||||
GL.BlendColor (0, 0, 0, tween);
|
||||
GL.BlendFunc (BlendingFactorSrc.ConstantAlpha, BlendingFactorDest.OneMinusConstantAlpha);
|
||||
// Blend function to draw the background frame
|
||||
GL.BlendColor(0, 0, 0, tween);
|
||||
GL.BlendFunc(BlendingFactorSrc.ConstantAlpha, BlendingFactorDest.OneMinusConstantAlpha);
|
||||
|
||||
// Draw the background frame
|
||||
GL.DrawArrays (BeginMode.TriangleStrip, 0, 4);
|
||||
// Draw the background frame
|
||||
GL.DrawArrays(BeginMode.TriangleStrip, 0, 4);
|
||||
|
||||
// Perform similar operations as above for the UV plane
|
||||
GL.UseProgram (ProgramUV);
|
||||
// Perform similar operations as above for the UV plane
|
||||
GL.UseProgram(ProgramUV);
|
||||
|
||||
GL.UniformMatrix4 (Uniforms [(int)Uniform.Render_Transform_UV], 1, false, preferredRenderTransform);
|
||||
GL.UniformMatrix4(Uniforms[(int)Uniform.Render_Transform_UV], 1, false, preferredRenderTransform);
|
||||
|
||||
GL.ActiveTexture (TextureUnit.Texture2);
|
||||
GL.BindTexture (foregroundChromaTexture.Target, foregroundChromaTexture.Name);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)All.ClampToEdge);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)All.ClampToEdge);
|
||||
// UV planes of foreground and background frame are used to render the UV plane of the destination frame
|
||||
GL.ActiveTexture(TextureUnit.Texture2);
|
||||
GL.BindTexture(foregroundChromaTexture.Target, foregroundChromaTexture.Name);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)All.ClampToEdge);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)All.ClampToEdge);
|
||||
|
||||
GL.ActiveTexture (TextureUnit.Texture3);
|
||||
GL.BindTexture (backgroundChromaTexture.Target, backgroundChromaTexture.Name);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)All.ClampToEdge);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)All.ClampToEdge);
|
||||
GL.ActiveTexture(TextureUnit.Texture3);
|
||||
GL.BindTexture(backgroundChromaTexture.Target, backgroundChromaTexture.Name);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)All.ClampToEdge);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)All.ClampToEdge);
|
||||
|
||||
GL.Viewport (0, 0, (int)destinationPixelBuffer.Width, (int)destinationPixelBuffer.Height);
|
||||
GL.Viewport(0, 0, (int)destinationPixelBuffer.GetWidthOfPlane(1), (int)destinationPixelBuffer.GetHeightOfPlane(1));
|
||||
|
||||
// Attach the destination texture as a color attachment to the off screen frame buffer
|
||||
GL.FramebufferTexture2D (FramebufferTarget.Framebuffer, FramebufferSlot.ColorAttachment0, destChromaTexture.Target, destChromaTexture.Name, 0);
|
||||
// Attach the destination texture as a color attachment to the off screen frame buffer
|
||||
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferSlot.ColorAttachment0, destChromaTexture.Target, destChromaTexture.Name, 0);
|
||||
|
||||
if (GL.CheckFramebufferStatus (FramebufferTarget.Framebuffer) != FramebufferErrorCode.FramebufferComplete) {
|
||||
Console.Error.WriteLine ("Failed to make complete framebuffer object " + GL.CheckFramebufferStatus (FramebufferTarget.Framebuffer));
|
||||
foregroundLumaTexture.Dispose ();
|
||||
foregroundChromaTexture.Dispose ();
|
||||
backgroundLumaTexture.Dispose ();
|
||||
backgroundChromaTexture.Dispose ();
|
||||
destLumaTexture.Dispose ();
|
||||
destChromaTexture.Dispose ();
|
||||
this.VideoTextureCache.Flush (0);
|
||||
EAGLContext.SetCurrentContext (null);
|
||||
return;
|
||||
}
|
||||
if (GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer) != FramebufferErrorCode.FramebufferComplete)
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to make complete framebuffer object {GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer)}");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
GL.ClearColor (0.0f, 0.0f, 0.0f, 1.0f);
|
||||
GL.Clear (ClearBufferMask.ColorBufferBit);
|
||||
GL.ClearColor(0f, 0f, 0f, 1f);
|
||||
GL.Clear(ClearBufferMask.ColorBufferBit);
|
||||
|
||||
GL.Uniform1 (Uniforms [(int)Uniform.UV], 2);
|
||||
GL.Uniform1(Uniforms[(int)Uniform.UV], 2);
|
||||
|
||||
GL.VertexAttribPointer ((int)Attrib.Vertex_UV, 2, VertexAttribPointerType.Float, false, 0, quadVertexData1);
|
||||
GL.EnableVertexAttribArray ((int)Attrib.Vertex_UV);
|
||||
GL.VertexAttribPointer((int)Attrib.Vertex_UV, 2, VertexAttribPointerType.Float, false, 0, quadVertexData1);
|
||||
GL.EnableVertexAttribArray((int)Attrib.Vertex_UV);
|
||||
|
||||
GL.VertexAttribPointer ((int)Attrib.TexCoord_UV, 2, VertexAttribPointerType.Float, false, 0, quadTextureData1);
|
||||
GL.EnableVertexAttribArray ((int)Attrib.TexCoord_UV);
|
||||
GL.VertexAttribPointer((int)Attrib.TexCoord_UV, 2, VertexAttribPointerType.Float, false, 0, quadTextureData1);
|
||||
GL.EnableVertexAttribArray((int)Attrib.TexCoord_UV);
|
||||
|
||||
// Blend function to draw the foreground frame
|
||||
GL.BlendFunc (BlendingFactorSrc.One, BlendingFactorDest.Zero);
|
||||
GL.BlendFunc(BlendingFactorSrc.One, BlendingFactorDest.Zero);
|
||||
|
||||
// Draw the foreground frame
|
||||
GL.DrawArrays (BeginMode.TriangleStrip, 0, 4);
|
||||
// Draw the foreground frame
|
||||
GL.DrawArrays(BeginMode.TriangleStrip, 0, 4);
|
||||
|
||||
GL.Uniform1 (Uniforms [(int)Uniform.UV], 3);
|
||||
GL.Uniform1(Uniforms[(int)Uniform.UV], 3);
|
||||
|
||||
GL.VertexAttribPointer ((int)Attrib.Vertex_UV, 2, VertexAttribPointerType.Float, false, 0, quadVertexData1);
|
||||
GL.EnableVertexAttribArray ((int)Attrib.Vertex_UV);
|
||||
GL.VertexAttribPointer((int)Attrib.Vertex_UV, 2, VertexAttribPointerType.Float, false, 0, quadVertexData1);
|
||||
GL.EnableVertexAttribArray((int)Attrib.Vertex_UV);
|
||||
|
||||
GL.VertexAttribPointer ((int)Attrib.TexCoord_UV, 2, VertexAttribPointerType.Float, false, 0, quadTextureData1);
|
||||
GL.EnableVertexAttribArray ((int)Attrib.TexCoord_UV);
|
||||
GL.VertexAttribPointer((int)Attrib.TexCoord_UV, 2, VertexAttribPointerType.Float, false, 0, quadTextureData1);
|
||||
GL.EnableVertexAttribArray((int)Attrib.TexCoord_UV);
|
||||
|
||||
// Blend function to draw the background frame
|
||||
GL.BlendColor (0, 0, 0, tween);
|
||||
GL.BlendFunc (BlendingFactorSrc.ConstantAlpha, BlendingFactorDest.OneMinusConstantAlpha);
|
||||
GL.BlendColor(0, 0, 0, tween);
|
||||
GL.BlendFunc(BlendingFactorSrc.ConstantAlpha, BlendingFactorDest.OneMinusConstantAlpha);
|
||||
|
||||
// Draw the background frame
|
||||
GL.DrawArrays (BeginMode.TriangleStrip, 0, 4);
|
||||
// Draw the background frame
|
||||
GL.DrawArrays(BeginMode.TriangleStrip, 0, 4);
|
||||
|
||||
GL.Flush ();
|
||||
GL.Flush();
|
||||
|
||||
foregroundLumaTexture.Dispose ();
|
||||
foregroundChromaTexture.Dispose ();
|
||||
backgroundLumaTexture.Dispose ();
|
||||
backgroundChromaTexture.Dispose ();
|
||||
destLumaTexture.Dispose ();
|
||||
destChromaTexture.Dispose ();
|
||||
this.VideoTextureCache.Flush (0);
|
||||
EAGLContext.SetCurrentContext (null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bail:
|
||||
foregroundLumaTexture.Dispose();
|
||||
foregroundChromaTexture.Dispose();
|
||||
backgroundLumaTexture.Dispose();
|
||||
backgroundChromaTexture.Dispose();
|
||||
destLumaTexture.Dispose();
|
||||
destChromaTexture.Dispose();
|
||||
|
||||
_videoTextureCache.Flush(0);
|
||||
EAGLContext.SetCurrentContext(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,274 +1,279 @@
|
|||
using System;
|
||||
using CoreGraphics;
|
||||
using OpenGLES;
|
||||
using OpenTK.Graphics.ES20;
|
||||
using System;
|
||||
|
||||
namespace AVCustomEdit
|
||||
{
|
||||
public class DiagonalWipeRenderer : OpenGLRenderer
|
||||
{
|
||||
const int ForegroundTrack = 0;
|
||||
const int BackgroundTrack = 1;
|
||||
public class DiagonalWipeRenderer : OpenGLRenderer
|
||||
{
|
||||
private const int ForegroundTrack = 0;
|
||||
private const int BackgroundTrack = 1;
|
||||
|
||||
CGPoint diagonalEnd1;
|
||||
CGPoint diagonalEnd2;
|
||||
private CGPoint diagonalEnd1;
|
||||
private CGPoint diagonalEnd2;
|
||||
|
||||
void quadVertexCoordinates (ref float[] vertexCoordinates, int trackID, float tween)
|
||||
{
|
||||
/*
|
||||
diagonalEnd1 and diagonalEnd2 represent the endpoints of a line which partitions the frame on screen into the two parts.
|
||||
private void QuadVertexCoordinates(ref float[] vertexCoordinates, int trackId, float tween)
|
||||
{
|
||||
/*
|
||||
diagonalEnd1 and diagonalEnd2 represent the endpoints of a line which partitions the frame on screen into the two parts.
|
||||
|
||||
diagonalEnd1
|
||||
------------X-----------
|
||||
| |
|
||||
| X diagonalEnd2
|
||||
| |
|
||||
| |
|
||||
------------------------
|
||||
diagonalEnd1
|
||||
------------X-----------
|
||||
| |
|
||||
| X diagonalEnd2
|
||||
| |
|
||||
| |
|
||||
------------------------
|
||||
|
||||
The below conditionals, use the tween factor as a measure to determine the size of the foreground and background quads.
|
||||
The below conditionals, use the tween factor as a measure to determine the size of the foreground and background quads.
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
if (tween <= 0.5f) { // The expectation here is that in half the timeRange of the transition we reach the diagonal of the frame
|
||||
diagonalEnd1 = new CGPoint (1f - tween * 4f, -1f);
|
||||
diagonalEnd2 = new CGPoint (1f, -1f + tween * 4f);
|
||||
if (tween <= 0.5f)
|
||||
{
|
||||
// The expectation here is that in half the timeRange of the transition we reach the diagonal of the frame
|
||||
diagonalEnd1 = new CGPoint(1f - tween * 4f, -1f);
|
||||
diagonalEnd2 = new CGPoint(1f, -1f + tween * 4f);
|
||||
|
||||
vertexCoordinates [6] = (float)diagonalEnd2.X;
|
||||
vertexCoordinates [7] = (float)diagonalEnd2.Y;
|
||||
vertexCoordinates [8] = (float)diagonalEnd1.X;
|
||||
vertexCoordinates [9] = (float)diagonalEnd1.Y;
|
||||
} else if (tween > 0.5f && tween < 1f) {
|
||||
if (trackID == ForegroundTrack) {
|
||||
diagonalEnd1 = new CGPoint (-1f, -1 + (tween - 0.5f) * 4f);
|
||||
diagonalEnd2 = new CGPoint (1f - (tween - 0.5f) * 4f, 1f);
|
||||
vertexCoordinates[6] = (float)diagonalEnd2.X;
|
||||
vertexCoordinates[7] = (float)diagonalEnd2.Y;
|
||||
vertexCoordinates[8] = (float)diagonalEnd1.X;
|
||||
vertexCoordinates[9] = (float)diagonalEnd1.Y;
|
||||
}
|
||||
else if (tween > 0.5f && tween < 1f)
|
||||
{
|
||||
if (trackId == ForegroundTrack)
|
||||
{
|
||||
diagonalEnd1 = new CGPoint(-1f, -1 + (tween - 0.5f) * 4f);
|
||||
diagonalEnd2 = new CGPoint(1f - (tween - 0.5f) * 4f, 1f);
|
||||
|
||||
vertexCoordinates [2] = (float)diagonalEnd2.X;
|
||||
vertexCoordinates [3] = (float)diagonalEnd2.Y;
|
||||
vertexCoordinates [4] = (float)diagonalEnd1.X;
|
||||
vertexCoordinates [5] = (float)diagonalEnd1.Y;
|
||||
vertexCoordinates [6] = (float)diagonalEnd1.X;
|
||||
vertexCoordinates [7] = (float)diagonalEnd1.Y;
|
||||
vertexCoordinates [8] = (float)diagonalEnd1.X;
|
||||
vertexCoordinates [9] = (float)diagonalEnd1.Y;
|
||||
} else if (trackID == BackgroundTrack) {
|
||||
vertexCoordinates [4] = 1f;
|
||||
vertexCoordinates [5] = 1f;
|
||||
vertexCoordinates [6] = -1f;
|
||||
vertexCoordinates [7] = -1f;
|
||||
}
|
||||
} else if (tween >= 1f) {
|
||||
diagonalEnd1 = new CGPoint (1f, -1f);
|
||||
diagonalEnd2 = new CGPoint (1f, -1f);
|
||||
}
|
||||
}
|
||||
vertexCoordinates[2] = (float)diagonalEnd2.X;
|
||||
vertexCoordinates[3] = (float)diagonalEnd2.Y;
|
||||
vertexCoordinates[4] = (float)diagonalEnd1.X;
|
||||
vertexCoordinates[5] = (float)diagonalEnd1.Y;
|
||||
vertexCoordinates[6] = (float)diagonalEnd1.X;
|
||||
vertexCoordinates[7] = (float)diagonalEnd1.Y;
|
||||
vertexCoordinates[8] = (float)diagonalEnd1.X;
|
||||
vertexCoordinates[9] = (float)diagonalEnd1.Y;
|
||||
}
|
||||
else if (trackId == BackgroundTrack)
|
||||
{
|
||||
vertexCoordinates[4] = 1f;
|
||||
vertexCoordinates[5] = 1f;
|
||||
vertexCoordinates[6] = -1f;
|
||||
vertexCoordinates[7] = -1f;
|
||||
}
|
||||
}
|
||||
else if (tween >= 1f)
|
||||
{
|
||||
diagonalEnd1 = new CGPoint(1f, -1f);
|
||||
diagonalEnd2 = new CGPoint(1f, -1f);
|
||||
}
|
||||
}
|
||||
|
||||
public override void RenderPixelBuffer (CoreVideo.CVPixelBuffer destinationPixelBuffer, CoreVideo.CVPixelBuffer foregroundPixelBuffer, CoreVideo.CVPixelBuffer backgroundPixelBuffer, float tween)
|
||||
{
|
||||
EAGLContext.SetCurrentContext (CurrentContext);
|
||||
public override void RenderPixelBuffer(CoreVideo.CVPixelBuffer destinationPixelBuffer, CoreVideo.CVPixelBuffer foregroundPixelBuffer, CoreVideo.CVPixelBuffer backgroundPixelBuffer, float tween)
|
||||
{
|
||||
EAGLContext.SetCurrentContext(CurrentContext);
|
||||
|
||||
if (foregroundPixelBuffer == null && backgroundPixelBuffer == null)
|
||||
return;
|
||||
if (foregroundPixelBuffer != null || backgroundPixelBuffer != null)
|
||||
{
|
||||
var foregroundLumaTexture = LumaTextureForPixelBuffer(foregroundPixelBuffer);
|
||||
var foregroundChromaTexture = ChromaTextureForPixelBuffer(foregroundPixelBuffer);
|
||||
|
||||
var foregroundLumaTexture = LumaTextureForPixelBuffer (foregroundPixelBuffer);
|
||||
var foregroundChromaTexture = ChromaTextureForPixelBuffer (foregroundPixelBuffer);
|
||||
var backgroundLumaTexture = LumaTextureForPixelBuffer(backgroundPixelBuffer);
|
||||
var backgroundChromaTexture = ChromaTextureForPixelBuffer(backgroundPixelBuffer);
|
||||
|
||||
var backgroundLumaTexture = LumaTextureForPixelBuffer (backgroundPixelBuffer);
|
||||
var backgroundChromaTexture = ChromaTextureForPixelBuffer (backgroundPixelBuffer);
|
||||
var destLumaTexture = LumaTextureForPixelBuffer(destinationPixelBuffer);
|
||||
var destChromaTexture = ChromaTextureForPixelBuffer(destinationPixelBuffer);
|
||||
|
||||
var destLumaTexture = LumaTextureForPixelBuffer (destinationPixelBuffer);
|
||||
var destChromaTexture = ChromaTextureForPixelBuffer (destinationPixelBuffer);
|
||||
GL.UseProgram(ProgramY);
|
||||
|
||||
GL.UseProgram (ProgramY);
|
||||
// Set the render transformq
|
||||
float[] preferredRenderTransform =
|
||||
{
|
||||
(float)RenderTransform.xx, (float) RenderTransform.xy, (float)RenderTransform.x0, 0.0f,
|
||||
(float)RenderTransform.yx, (float) RenderTransform.yy, (float)RenderTransform.y0, 0.0f,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f
|
||||
};
|
||||
|
||||
// Set the render transformq
|
||||
float[] preferredRenderTransform = {
|
||||
(float)RenderTransform.xx,(float) RenderTransform.xy, (float)RenderTransform.x0, 0.0f,
|
||||
(float)RenderTransform.yx, (float)RenderTransform.yy, (float)RenderTransform.y0, 0.0f,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f
|
||||
};
|
||||
GL.UniformMatrix4(Uniforms[(int)Uniform.Y], 1, false, preferredRenderTransform);
|
||||
|
||||
GL.UniformMatrix4 (Uniforms [(int)Uniform.Y], 1, false, preferredRenderTransform);
|
||||
GL.BindFramebuffer(FramebufferTarget.Framebuffer, (int)OffscreenBufferHandle);
|
||||
|
||||
GL.BindFramebuffer (FramebufferTarget.Framebuffer, (int)OffscreenBufferHandle);
|
||||
GL.Viewport(0, 0, (int)destinationPixelBuffer.GetWidthOfPlane(0), (int)destinationPixelBuffer.GetHeightOfPlane(0));
|
||||
|
||||
GL.Viewport (0, 0, (int)destinationPixelBuffer.GetWidthOfPlane (0), (int)destinationPixelBuffer.GetHeightOfPlane (0));
|
||||
// Y planes of foreground and background frame are used to render the Y plane of the destination frame
|
||||
GL.ActiveTexture(TextureUnit.Texture0);
|
||||
GL.BindTexture(foregroundLumaTexture.Target, foregroundLumaTexture.Name);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)All.ClampToEdge);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)All.ClampToEdge);
|
||||
|
||||
// Y planes of foreground and background frame are used to render the Y plane of the destination frame
|
||||
GL.ActiveTexture (TextureUnit.Texture0);
|
||||
GL.BindTexture (foregroundLumaTexture.Target, foregroundLumaTexture.Name);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)All.ClampToEdge);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)All.ClampToEdge);
|
||||
GL.ActiveTexture(TextureUnit.Texture1);
|
||||
GL.BindTexture(backgroundLumaTexture.Target, backgroundLumaTexture.Name);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)All.ClampToEdge);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)All.ClampToEdge);
|
||||
|
||||
GL.ActiveTexture (TextureUnit.Texture1);
|
||||
GL.BindTexture (backgroundLumaTexture.Target, backgroundLumaTexture.Name);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)All.ClampToEdge);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)All.ClampToEdge);
|
||||
// Attach the destination texture as a color attachment to the off screen frame buffer
|
||||
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer,
|
||||
FramebufferSlot.ColorAttachment0,
|
||||
destLumaTexture.Target,
|
||||
destLumaTexture.Name,
|
||||
0);
|
||||
|
||||
GL.FramebufferTexture2D (FramebufferTarget.Framebuffer, FramebufferSlot.ColorAttachment0,
|
||||
destLumaTexture.Target, destLumaTexture.Name, 0);
|
||||
if (GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer) != FramebufferErrorCode.FramebufferComplete)
|
||||
{
|
||||
Console.WriteLine("Failed to make complete frmaebuffer object: {0}", GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer));
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (GL.CheckFramebufferStatus (FramebufferTarget.Framebuffer) != FramebufferErrorCode.FramebufferComplete) {
|
||||
Console.WriteLine ("Failed to make complete frmaebuffer object: {0}", GL.CheckFramebufferStatus (FramebufferTarget.Framebuffer));
|
||||
GL.ClearColor(0f, 0f, 0f, 1f);
|
||||
GL.Clear(ClearBufferMask.ColorBufferBit);
|
||||
|
||||
foregroundLumaTexture.Dispose ();
|
||||
foregroundChromaTexture.Dispose ();
|
||||
backgroundLumaTexture.Dispose ();
|
||||
backgroundChromaTexture.Dispose ();
|
||||
destLumaTexture.Dispose ();
|
||||
destChromaTexture.Dispose ();
|
||||
float[] quadVertexData1 =
|
||||
{
|
||||
-1.0f, 1.0f,
|
||||
1.0f, 1.0f,
|
||||
-1.0f, -1.0f,
|
||||
1.0f, -1.0f,
|
||||
1.0f, -1.0f,
|
||||
};
|
||||
|
||||
// Periodic texture cache flush every frame
|
||||
VideoTextureCache.Flush (CoreVideo.CVOptionFlags.None);
|
||||
// Compute the vertex data for the foreground frame at this instructionLerp
|
||||
QuadVertexCoordinates(ref quadVertexData1, ForegroundTrack, tween);
|
||||
|
||||
EAGLContext.SetCurrentContext (null);
|
||||
}
|
||||
// texture data varies from 0 -> 1, whereas vertex data varies from -1 -> 1
|
||||
float[] quadTextureData1 =
|
||||
{
|
||||
0.5f + quadVertexData1[0] / 2f, 0.5f + quadVertexData1[1] / 2f,
|
||||
0.5f + quadVertexData1[2] / 2f, 0.5f + quadVertexData1[3] / 2f,
|
||||
0.5f + quadVertexData1[4] / 2f, 0.5f + quadVertexData1[5] / 2f,
|
||||
0.5f + quadVertexData1[6] / 2f, 0.5f + quadVertexData1[7] / 2f,
|
||||
0.5f + quadVertexData1[8] / 2f, 0.5f + quadVertexData1[9] / 2f,
|
||||
};
|
||||
|
||||
GL.ClearColor (0f, 0f, 0f, 1f);
|
||||
GL.Clear (ClearBufferMask.ColorBufferBit);
|
||||
GL.Uniform1(Uniforms[(int)Uniform.Y], 0f);
|
||||
|
||||
float[] quadVertexData1 = {
|
||||
-1.0f, 1.0f,
|
||||
1.0f, 1.0f,
|
||||
-1.0f, -1.0f,
|
||||
1.0f, -1.0f,
|
||||
1.0f, -1.0f,
|
||||
};
|
||||
// Compute the vertex data for the foreground frame at this instructionLerp
|
||||
quadVertexCoordinates (ref quadVertexData1, ForegroundTrack, tween);
|
||||
GL.VertexAttribPointer<float>((int)Attrib.Vertex_Y, 2, VertexAttribPointerType.Float, false, 0, quadVertexData1);
|
||||
GL.EnableVertexAttribArray((int)Attrib.Vertex_Y);
|
||||
|
||||
// texture data varies from 0 -> 1, whereas vertex data varies from -1 -> 1
|
||||
float[] quadTextureData1 = {
|
||||
0.5f + quadVertexData1[0] / 2f, 0.5f + quadVertexData1[1] / 2f,
|
||||
0.5f + quadVertexData1[2] / 2f, 0.5f + quadVertexData1[3] / 2f,
|
||||
0.5f + quadVertexData1[4] / 2f, 0.5f + quadVertexData1[5] / 2f,
|
||||
0.5f + quadVertexData1[6] / 2f, 0.5f + quadVertexData1[7] / 2f,
|
||||
0.5f + quadVertexData1[8] / 2f, 0.5f + quadVertexData1[9] / 2f,
|
||||
};
|
||||
GL.VertexAttribPointer<float>((int)Attrib.TexCoord_Y, 2, VertexAttribPointerType.Float, false, 0, quadTextureData1);
|
||||
GL.EnableVertexAttribArray((int)Attrib.TexCoord_Y);
|
||||
|
||||
GL.Uniform1 (Uniforms [(int)Uniform.Y], 0f);
|
||||
// Draw the foreground frame
|
||||
GL.DrawArrays(BeginMode.TriangleStrip, 0, 5);
|
||||
|
||||
GL.VertexAttribPointer<float> ((int)Attrib.Vertex_Y, 2, VertexAttribPointerType.Float, false, 0, quadVertexData1);
|
||||
GL.EnableVertexAttribArray ((int)Attrib.Vertex_Y);
|
||||
float[] quadVertexData2 =
|
||||
{
|
||||
(float)diagonalEnd2.X, (float)diagonalEnd2.Y,
|
||||
(float)diagonalEnd1.X, (float)diagonalEnd1.Y,
|
||||
1.0f, -1.0f,
|
||||
1.0f, -1.0f,
|
||||
1.0f, -1.0f,
|
||||
};
|
||||
|
||||
GL.VertexAttribPointer<float> ((int)Attrib.TexCoord_Y, 2, VertexAttribPointerType.Float, false, 0, quadTextureData1);
|
||||
GL.EnableVertexAttribArray ((int)Attrib.TexCoord_Y);
|
||||
// Compute the vertex data for the background frame at this instructionLerp
|
||||
QuadVertexCoordinates(ref quadVertexData2, BackgroundTrack, tween);
|
||||
|
||||
GL.DrawArrays (BeginMode.TriangleStrip, 0, 5);
|
||||
float[] quadTextureData2 =
|
||||
{
|
||||
0.5f + quadVertexData2[0] / 2f, 0.5f + quadVertexData2[1] / 2f,
|
||||
0.5f + quadVertexData2[2] / 2f, 0.5f + quadVertexData2[3] / 2f,
|
||||
0.5f + quadVertexData2[4] / 2f, 0.5f + quadVertexData2[5] / 2f,
|
||||
0.5f + quadVertexData2[6] / 2f, 0.5f + quadVertexData2[7] / 2f,
|
||||
0.5f + quadVertexData2[8] / 2f, 0.5f + quadVertexData2[9] / 2f,
|
||||
};
|
||||
|
||||
float[] quadVertexData2 = {
|
||||
(float)diagonalEnd2.X, (float)diagonalEnd2.Y,
|
||||
(float)diagonalEnd1.X, (float)diagonalEnd1.Y,
|
||||
1.0f, -1.0f,
|
||||
1.0f, -1.0f,
|
||||
1.0f, -1.0f,
|
||||
};
|
||||
GL.Uniform1(Uniforms[(int)Uniform.Y], 1);
|
||||
|
||||
quadVertexCoordinates (ref quadVertexData2, BackgroundTrack, tween);
|
||||
GL.VertexAttribPointer<float>((int)Attrib.Vertex_Y, 2, VertexAttribPointerType.Float, false, 0, quadVertexData2);
|
||||
GL.EnableVertexAttribArray((int)Attrib.Vertex_Y);
|
||||
|
||||
float[] quadTextureData2 = {
|
||||
0.5f + quadVertexData2[0] / 2f, 0.5f + quadVertexData2[1] / 2f,
|
||||
0.5f + quadVertexData2[2] / 2f, 0.5f + quadVertexData2[3] / 2f,
|
||||
0.5f + quadVertexData2[4] / 2f, 0.5f + quadVertexData2[5] / 2f,
|
||||
0.5f + quadVertexData2[6] / 2f, 0.5f + quadVertexData2[7] / 2f,
|
||||
0.5f + quadVertexData2[8] / 2f, 0.5f + quadVertexData2[9] / 2f,
|
||||
};
|
||||
GL.VertexAttribPointer<float>((int)Attrib.TexCoord_Y, 2, VertexAttribPointerType.Float, false, 0, quadTextureData2);
|
||||
GL.EnableVertexAttribArray((int)Attrib.TexCoord_Y);
|
||||
|
||||
GL.Uniform1 (Uniforms [(int)Uniform.Y], 1);
|
||||
// Draw the background frame
|
||||
GL.DrawArrays(BeginMode.TriangleStrip, 0, 5);
|
||||
|
||||
GL.VertexAttribPointer<float> ((int)Attrib.Vertex_Y, 2, VertexAttribPointerType.Float, false, 0, quadVertexData2);
|
||||
GL.EnableVertexAttribArray ((int)Attrib.Vertex_Y);
|
||||
// Perform similar operations as above for the UV plane
|
||||
GL.UseProgram(ProgramUV);
|
||||
|
||||
GL.VertexAttribPointer<float> ((int)Attrib.TexCoord_Y, 2, VertexAttribPointerType.Float, false, 0, quadTextureData2);
|
||||
GL.EnableVertexAttribArray ((int)Attrib.TexCoord_Y);
|
||||
GL.UniformMatrix4(Uniforms[(int)Uniform.Render_Transform_UV], 1, false, preferredRenderTransform);
|
||||
|
||||
// Draw the background frame
|
||||
GL.DrawArrays (BeginMode.TriangleStrip, 0, 5);
|
||||
// UV planes of foreground and background frame are used to render the UV plane of the destination frame
|
||||
GL.ActiveTexture(TextureUnit.Texture2);
|
||||
GL.BindTexture(foregroundChromaTexture.Target, foregroundChromaTexture.Name);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)All.ClampToEdge);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)All.ClampToEdge);
|
||||
|
||||
// Perform similar operations as above for the UV plane
|
||||
GL.UseProgram (ProgramUV);
|
||||
GL.ActiveTexture(TextureUnit.Texture3);
|
||||
GL.BindTexture(backgroundChromaTexture.Target, backgroundChromaTexture.Name);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)All.ClampToEdge);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)All.ClampToEdge);
|
||||
|
||||
GL.UniformMatrix4 (Uniforms [(int)Uniform.Render_Transform_UV], 1, false, preferredRenderTransform);
|
||||
GL.Viewport(0, 0, (int)destinationPixelBuffer.GetWidthOfPlane(1), (int)destinationPixelBuffer.GetHeightOfPlane(1));
|
||||
|
||||
// UV planes of foreground and background frame are used to render the UV plane of the destination frame
|
||||
GL.ActiveTexture (TextureUnit.Texture2);
|
||||
GL.BindTexture (foregroundChromaTexture.Target, foregroundChromaTexture.Name);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)All.ClampToEdge);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)All.ClampToEdge);
|
||||
// Attach the destination texture as a color attachment to the off screen frame buffer
|
||||
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer,
|
||||
FramebufferSlot.ColorAttachment0,
|
||||
destChromaTexture.Target,
|
||||
destChromaTexture.Name,
|
||||
0);
|
||||
|
||||
GL.ActiveTexture (TextureUnit.Texture3);
|
||||
GL.BindTexture (backgroundChromaTexture.Target, backgroundChromaTexture.Name);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)All.ClampToEdge);
|
||||
GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)All.ClampToEdge);
|
||||
if (GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer) != FramebufferErrorCode.FramebufferComplete)
|
||||
{
|
||||
Console.WriteLine("Failed to make complete framebuffer object: {0}", GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer));
|
||||
goto bail;
|
||||
}
|
||||
|
||||
GL.Viewport (0, 0,(int) destinationPixelBuffer.GetWidthOfPlane (1), (int)destinationPixelBuffer.GetHeightOfPlane (1));
|
||||
GL.ClearColor(0f, 0f, 0f, 1f);
|
||||
GL.Clear(ClearBufferMask.ColorBufferBit);
|
||||
|
||||
// Attach the destination texture as a color attachment to the off screen frame buffer
|
||||
GL.FramebufferTexture2D (FramebufferTarget.Framebuffer, FramebufferSlot.ColorAttachment0, destChromaTexture.Target,
|
||||
destChromaTexture.Name, 0);
|
||||
GL.Uniform1(Uniforms[(int)Uniform.UV], 2);
|
||||
|
||||
if (GL.CheckFramebufferStatus (FramebufferTarget.Framebuffer) != FramebufferErrorCode.FramebufferComplete) {
|
||||
Console.WriteLine ("Failed to make complete framebuffer object: {0}",
|
||||
GL.CheckFramebufferStatus (FramebufferTarget.Framebuffer));
|
||||
GL.VertexAttribPointer<float>((int)Attrib.Vertex_UV, 2, VertexAttribPointerType.Float, false, 0, quadVertexData1);
|
||||
GL.EnableVertexAttribArray((int)Attrib.Vertex_UV);
|
||||
|
||||
foregroundLumaTexture.Dispose ();
|
||||
foregroundChromaTexture.Dispose ();
|
||||
backgroundLumaTexture.Dispose ();
|
||||
backgroundChromaTexture.Dispose ();
|
||||
destLumaTexture.Dispose ();
|
||||
destChromaTexture.Dispose ();
|
||||
GL.VertexAttribPointer<float>((int)Attrib.TexCoord_UV, 2, VertexAttribPointerType.Float, false, 0, quadTextureData1);
|
||||
GL.EnableVertexAttribArray((int)Attrib.TexCoord_UV);
|
||||
|
||||
// Periodic texture cache flush every frame
|
||||
VideoTextureCache.Flush (CoreVideo.CVOptionFlags.None);
|
||||
// Draw the foreground frame
|
||||
GL.DrawArrays(BeginMode.TriangleStrip, 0, 5);
|
||||
|
||||
EAGLContext.SetCurrentContext (null);
|
||||
}
|
||||
GL.Uniform1(Uniforms[(int)Uniform.UV], 3);
|
||||
|
||||
GL.ClearColor (0f, 0f, 0f, 1f);
|
||||
GL.Clear (ClearBufferMask.ColorBufferBit);
|
||||
GL.VertexAttribPointer<float>((int)Attrib.Vertex_UV, 2, VertexAttribPointerType.Float, false, 0, quadVertexData2);
|
||||
GL.EnableVertexAttribArray((int)Attrib.Vertex_UV);
|
||||
|
||||
GL.Uniform1 (Uniforms [(int)Uniform.UV], 2);
|
||||
GL.VertexAttribPointer<float>((int)Attrib.TexCoord_UV, 2, VertexAttribPointerType.Float, false, 0, quadTextureData2);
|
||||
GL.EnableVertexAttribArray((int)Attrib.TexCoord_UV);
|
||||
|
||||
GL.VertexAttribPointer<float> ((int)Attrib.Vertex_UV, 2, VertexAttribPointerType.Float, false, 0, quadVertexData1);
|
||||
GL.EnableVertexAttribArray ((int)Attrib.Vertex_UV);
|
||||
// Draw the background frame
|
||||
GL.DrawArrays(BeginMode.TriangleStrip, 0, 5);
|
||||
|
||||
GL.VertexAttribPointer<float> ((int)Attrib.TexCoord_UV, 2, VertexAttribPointerType.Float, false, 0, quadTextureData1);
|
||||
GL.EnableVertexAttribArray ((int)Attrib.TexCoord_UV);
|
||||
GL.Flush();
|
||||
|
||||
GL.DrawArrays (BeginMode.TriangleStrip, 0, 5);
|
||||
bail:
|
||||
foregroundLumaTexture.Dispose();
|
||||
foregroundChromaTexture.Dispose();
|
||||
backgroundLumaTexture.Dispose();
|
||||
backgroundChromaTexture.Dispose();
|
||||
destLumaTexture.Dispose();
|
||||
destChromaTexture.Dispose();
|
||||
|
||||
GL.Uniform1 (Uniforms [(int)Uniform.UV], 3);
|
||||
|
||||
GL.VertexAttribPointer<float> ((int)Attrib.Vertex_UV, 2, VertexAttribPointerType.Float, false, 0, quadVertexData2);
|
||||
GL.EnableVertexAttribArray ((int)Attrib.Vertex_UV);
|
||||
|
||||
GL.VertexAttribPointer<float> ((int)Attrib.TexCoord_UV, 2, VertexAttribPointerType.Float, false, 0, quadTextureData2);
|
||||
GL.EnableVertexAttribArray ((int)Attrib.TexCoord_UV);
|
||||
|
||||
GL.DrawArrays (BeginMode.TriangleStrip, 0, 5);
|
||||
|
||||
GL.Flush ();
|
||||
|
||||
foregroundLumaTexture.Dispose ();
|
||||
foregroundChromaTexture.Dispose ();
|
||||
backgroundLumaTexture.Dispose ();
|
||||
backgroundChromaTexture.Dispose ();
|
||||
destLumaTexture.Dispose ();
|
||||
destChromaTexture.Dispose ();
|
||||
|
||||
// Periodic texture cache flush every frame
|
||||
VideoTextureCache.Flush (CoreVideo.CVOptionFlags.None);
|
||||
|
||||
EAGLContext.SetCurrentContext (null);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Periodic texture cache flush every frame
|
||||
_videoTextureCache.Flush(0);
|
||||
EAGLContext.SetCurrentContext(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,294 +1,330 @@
|
|||
using System;
|
||||
using Foundation;
|
||||
using OpenGLES;
|
||||
using CoreGraphics;
|
||||
using CoreVideo;
|
||||
using Foundation;
|
||||
using OpenGLES;
|
||||
using OpenTK.Graphics.ES20;
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace AVCustomEdit
|
||||
{
|
||||
public class OpenGLRenderer : NSObject
|
||||
{
|
||||
public int ProgramY = -1;
|
||||
public int ProgramUV = -1;
|
||||
public CGAffineTransform RenderTransform;
|
||||
public CVOpenGLESTextureCache VideoTextureCache;
|
||||
public EAGLContext CurrentContext;
|
||||
public uint OffscreenBufferHandle;
|
||||
public int[] Uniforms = new int[(int)Uniform.Num_Uniforms];
|
||||
public class OpenGLRenderer : NSObject
|
||||
{
|
||||
private const string VertShaderSource = @"attribute vec4 position;
|
||||
attribute vec2 texCoord;
|
||||
uniform mat4 renderTransform;
|
||||
varying vec2 texCoordVarying;
|
||||
void main()
|
||||
{
|
||||
gl_Position = position * renderTransform;
|
||||
texCoordVarying = texCoord;
|
||||
}";
|
||||
|
||||
const string vertShaderSource = @"attribute vec4 position;
|
||||
attribute vec2 texCoord;
|
||||
uniform mat4 renderTransform;
|
||||
varying vec2 texCoordVarying;
|
||||
void main()
|
||||
{
|
||||
gl_Position = position * renderTransform;
|
||||
texCoordVarying = texCoord;
|
||||
}";
|
||||
const string fragShaderYSource = @"varying highp vec2 texCoordVarying;
|
||||
uniform sampler2D SamplerY;
|
||||
void main()
|
||||
{
|
||||
gl_FragColor.r = texture2D(SamplerY, texCoordVarying).r;
|
||||
}";
|
||||
const string fragShaderUVSource = @"varying highp vec2 texCoordVarying;
|
||||
uniform sampler2D SamplerUV;
|
||||
void main()
|
||||
{
|
||||
gl_FragColor.rg = texture2D(SamplerUV, texCoordVarying).rg;
|
||||
}";
|
||||
private const string FragShaderYSource = @"varying highp vec2 texCoordVarying;
|
||||
uniform sampler2D SamplerY;
|
||||
void main()
|
||||
{
|
||||
gl_FragColor.r = texture2D(SamplerY, texCoordVarying).r;
|
||||
}";
|
||||
|
||||
public OpenGLRenderer ()
|
||||
{
|
||||
CurrentContext = new EAGLContext (EAGLRenderingAPI.OpenGLES2);
|
||||
private const string FragShaderUVSource = @"varying highp vec2 texCoordVarying;
|
||||
uniform sampler2D SamplerUV;
|
||||
void main()
|
||||
{
|
||||
gl_FragColor.rg = texture2D(SamplerUV, texCoordVarying).rg;
|
||||
}";
|
||||
|
||||
EAGLContext.SetCurrentContext (CurrentContext);
|
||||
public uint OffscreenBufferHandle;
|
||||
|
||||
SetupOffScreenRenderContext();
|
||||
loadShaders ();
|
||||
public OpenGLRenderer()
|
||||
{
|
||||
CurrentContext = new EAGLContext(EAGLRenderingAPI.OpenGLES2);
|
||||
EAGLContext.SetCurrentContext(CurrentContext);
|
||||
|
||||
EAGLContext.SetCurrentContext (null);
|
||||
SetupOffScreenRenderContext();
|
||||
LoadShaders();
|
||||
|
||||
}
|
||||
EAGLContext.SetCurrentContext(null);
|
||||
}
|
||||
|
||||
public virtual CVOpenGLESTexture LumaTextureForPixelBuffer (CVPixelBuffer pixelBuffer)
|
||||
{
|
||||
CVOpenGLESTexture lumaTexture = null;
|
||||
CVReturn err;
|
||||
if (VideoTextureCache == null) {
|
||||
Console.Error.WriteLine ("No video texture cache");
|
||||
return lumaTexture;
|
||||
}
|
||||
// Periodic texture cache flush every frame
|
||||
VideoTextureCache.Flush (0);
|
||||
public int ProgramY { get; set; } = -1;
|
||||
public int ProgramUV { get; set; } = -1;
|
||||
public CGAffineTransform RenderTransform { get; set; }
|
||||
public CVOpenGLESTextureCache _videoTextureCache { get; set; }
|
||||
public EAGLContext CurrentContext { get; set; }
|
||||
public int[] Uniforms { get; set; } = new int[(int)Uniform.Num_Uniforms];
|
||||
|
||||
// CVOpenGLTextureCacheCreateTextureFromImage will create GL texture optimally from CVPixelBufferRef.
|
||||
// UV
|
||||
lumaTexture = VideoTextureCache.TextureFromImage (pixelBuffer, true, All.RedExt, (int)pixelBuffer.Width,(int) pixelBuffer.Height, All.RedExt, DataType.UnsignedByte, 0, out err);
|
||||
if (lumaTexture == null || err != CVReturn.Success)
|
||||
Console.Error.WriteLine ("Error at creating luma texture using CVOpenGLESTextureCacheCreateTextureFromImage: {0}", err);
|
||||
public virtual void RenderPixelBuffer(CVPixelBuffer destinationPixelBuffer, CVPixelBuffer foregroundPixelBuffer, CVPixelBuffer backgroundPixelBuffer, float tween)
|
||||
{
|
||||
DoesNotRecognizeSelector(new ObjCRuntime.Selector("_cmd"));
|
||||
}
|
||||
|
||||
return lumaTexture;
|
||||
}
|
||||
public void SetupOffScreenRenderContext()
|
||||
{
|
||||
//-- Create CVOpenGLESTextureCacheRef for optimal CVPixelBufferRef to GLES texture conversion.
|
||||
if (_videoTextureCache != null)
|
||||
{
|
||||
_videoTextureCache.Dispose();
|
||||
_videoTextureCache = null;
|
||||
}
|
||||
|
||||
public virtual CVOpenGLESTexture ChromaTextureForPixelBuffer (CVPixelBuffer pixelBuffer)
|
||||
{
|
||||
CVOpenGLESTexture chromaTexture = null;
|
||||
CVReturn err;
|
||||
if (VideoTextureCache == null) {
|
||||
Console.Error.WriteLine ("No video texture cache");
|
||||
return chromaTexture;
|
||||
}
|
||||
// Periodic texture cache flush every frame
|
||||
VideoTextureCache.Flush (0);
|
||||
_videoTextureCache = CVOpenGLESTextureCache.FromEAGLContext(CurrentContext);
|
||||
|
||||
// CVOpenGLTextureCacheCreateTextureFromImage will create GL texture optimally from CVPixelBufferRef.
|
||||
// UV
|
||||
var height = pixelBuffer.GetHeightOfPlane (1);
|
||||
var width = pixelBuffer.GetWidthOfPlane (1);
|
||||
chromaTexture = VideoTextureCache.TextureFromImage (pixelBuffer, true, All.RgExt, (int)width, (int)height, All.RgExt, DataType.UnsignedByte, 1, out err);
|
||||
GL.Disable(EnableCap.DepthTest);
|
||||
GL.GenFramebuffers(1, out OffscreenBufferHandle);
|
||||
GL.BindFramebuffer(FramebufferTarget.Framebuffer, OffscreenBufferHandle);
|
||||
}
|
||||
|
||||
if (chromaTexture == null || err != CVReturn.Success)
|
||||
Console.Error.WriteLine ("Error at creating chroma texture using CVOpenGLESTextureCacheCreateTextureFromImage: {0}", err);
|
||||
public virtual CVOpenGLESTexture LumaTextureForPixelBuffer(CVPixelBuffer pixelBuffer)
|
||||
{
|
||||
CVOpenGLESTexture lumaTexture = null;
|
||||
if (_videoTextureCache == null)
|
||||
{
|
||||
Console.Error.WriteLine("No video texture cache");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
return chromaTexture;
|
||||
}
|
||||
// Periodic texture cache flush every frame
|
||||
_videoTextureCache.Flush(0);
|
||||
|
||||
public virtual void RenderPixelBuffer(CVPixelBuffer destinationPixelBuffer, CVPixelBuffer foregroundPixelBuffer, CVPixelBuffer backgroundPixelBuffer, float tween)
|
||||
{
|
||||
DoesNotRecognizeSelector (new ObjCRuntime.Selector ("_cmd"));
|
||||
}
|
||||
// CVOpenGLTextureCacheCreateTextureFromImage will create GL texture optimally from CVPixelBufferRef.
|
||||
// Y
|
||||
lumaTexture = _videoTextureCache.TextureFromImage(pixelBuffer,
|
||||
true,
|
||||
All.RedExt,
|
||||
(int)pixelBuffer.Width,
|
||||
(int)pixelBuffer.Height,
|
||||
All.RedExt,
|
||||
DataType.UnsignedByte,
|
||||
0,
|
||||
out CVReturn error);
|
||||
|
||||
public void SetupOffScreenRenderContext()
|
||||
{
|
||||
//-- Create CVOpenGLESTextureCacheRef for optimal CVPixelBufferRef to GLES texture conversion.
|
||||
if (VideoTextureCache != null) {
|
||||
VideoTextureCache.Dispose ();
|
||||
VideoTextureCache = null;
|
||||
}
|
||||
if (lumaTexture == null || error != CVReturn.Success)
|
||||
{
|
||||
Console.Error.WriteLine($"Error at creating luma texture using CVOpenGLESTextureCacheCreateTextureFromImage: {error}");
|
||||
}
|
||||
|
||||
VideoTextureCache = CVOpenGLESTextureCache.FromEAGLContext (CurrentContext);
|
||||
GL.Disable (EnableCap.DepthTest);
|
||||
GL.GenFramebuffers (1, out OffscreenBufferHandle);
|
||||
GL.BindFramebuffer (FramebufferTarget.Framebuffer, OffscreenBufferHandle);
|
||||
}
|
||||
bail:
|
||||
return lumaTexture;
|
||||
}
|
||||
|
||||
// OpenGL ES 2 shader compilation
|
||||
bool loadShaders()
|
||||
{
|
||||
int vertShader, fragShaderY, fragShaderUV;
|
||||
public virtual CVOpenGLESTexture ChromaTextureForPixelBuffer(CVPixelBuffer pixelBuffer)
|
||||
{
|
||||
CVOpenGLESTexture chromaTexture = null;
|
||||
if (_videoTextureCache == null)
|
||||
{
|
||||
Console.Error.WriteLine("No video texture cache");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
// Create the shader program.
|
||||
ProgramY = GL.CreateProgram ();
|
||||
ProgramUV = GL.CreateProgram ();
|
||||
// Periodic texture cache flush every frame
|
||||
_videoTextureCache.Flush(0);
|
||||
|
||||
// Create and compile the vertex shader.
|
||||
// CVOpenGLTextureCacheCreateTextureFromImage will create GL texture optimally from CVPixelBufferRef.
|
||||
// UV
|
||||
var height = pixelBuffer.GetHeightOfPlane(1);
|
||||
var width = pixelBuffer.GetWidthOfPlane(1);
|
||||
chromaTexture = _videoTextureCache.TextureFromImage(pixelBuffer,
|
||||
true,
|
||||
All.RgExt,
|
||||
(int)width,
|
||||
(int)height,
|
||||
All.RgExt,
|
||||
DataType.UnsignedByte,
|
||||
1,
|
||||
out CVReturn error);
|
||||
|
||||
if (!CompileShader (ShaderType.VertexShader, vertShaderSource, out vertShader)) {
|
||||
Console.Error.WriteLine ("Failed to compile vertex shader");
|
||||
return false;
|
||||
}
|
||||
if (chromaTexture == null || error != CVReturn.Success)
|
||||
{
|
||||
Console.Error.WriteLine($"Error at creating chroma texture using CVOpenGLESTextureCacheCreateTextureFromImage: {error}");
|
||||
}
|
||||
|
||||
if(!CompileShader (ShaderType.FragmentShader, fragShaderYSource, out fragShaderY)) {
|
||||
Console.Error.WriteLine ("Failed to compile Y fragment shader");
|
||||
return false;
|
||||
}
|
||||
bail:
|
||||
return chromaTexture;
|
||||
}
|
||||
|
||||
if(!CompileShader (ShaderType.FragmentShader, fragShaderUVSource, out fragShaderUV)) {
|
||||
Console.Error.WriteLine ("Failed to compile UV fragment shader");
|
||||
return false;
|
||||
}
|
||||
#region OpenGL ES 2 shader compilation
|
||||
|
||||
GL.AttachShader (ProgramY, vertShader);
|
||||
GL.AttachShader (ProgramY, fragShaderY);
|
||||
private bool LoadShaders()
|
||||
{
|
||||
int vertShader, fragShaderY, fragShaderUV;
|
||||
|
||||
GL.AttachShader (ProgramUV, vertShader);
|
||||
GL.AttachShader (ProgramUV, fragShaderUV);
|
||||
// Create the shader program.
|
||||
ProgramY = GL.CreateProgram();
|
||||
ProgramUV = GL.CreateProgram();
|
||||
|
||||
// Bind attribute locations. This needs to be done prior to linking.
|
||||
GL.BindAttribLocation (ProgramY,(int) Attrib.Vertex_Y, "position");
|
||||
GL.BindAttribLocation (ProgramY,(int) Attrib.TexCoord_Y, "texCoord");
|
||||
GL.BindAttribLocation (ProgramUV,(int) Attrib.Vertex_UV, "position");
|
||||
GL.BindAttribLocation (ProgramUV, (int)Attrib.TexCoord_UV, "texCoord");
|
||||
// Create and compile the vertex shader.
|
||||
if (!CompileShader(ShaderType.VertexShader, VertShaderSource, out vertShader))
|
||||
{
|
||||
Console.Error.WriteLine("Failed to compile vertex shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Link the program.
|
||||
if (!LinkProgram (ProgramY) || !LinkProgram (ProgramUV)) {
|
||||
Console.Error.WriteLine ("Failed to link program");
|
||||
if (vertShader != 0) {
|
||||
GL.DeleteShader (vertShader);
|
||||
vertShader = 0;
|
||||
}
|
||||
if (fragShaderY != 0) {
|
||||
GL.DeleteShader (fragShaderY);
|
||||
fragShaderY = 0;
|
||||
}
|
||||
if (fragShaderUV != 0) {
|
||||
GL.DeleteShader (fragShaderUV);
|
||||
fragShaderUV = 0;
|
||||
}
|
||||
if (ProgramY != 0) {
|
||||
GL.DeleteProgram (ProgramY);
|
||||
ProgramY = 0;
|
||||
}
|
||||
if (ProgramUV != 0) {
|
||||
GL.DeleteProgram (ProgramUV);
|
||||
ProgramUV = 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!CompileShader(ShaderType.FragmentShader, FragShaderYSource, out fragShaderY))
|
||||
{
|
||||
Console.Error.WriteLine("Failed to compile Y fragment shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get uniform locations.
|
||||
Uniforms [(int)Uniform.Y] = GL.GetUniformLocation (ProgramY, "SamplerY");
|
||||
Uniforms [(int)Uniform.UV] = GL.GetUniformLocation (ProgramUV, "SamplerUV");
|
||||
Uniforms [(int)Uniform.Render_Transform_Y] = GL.GetUniformLocation (ProgramY, "renderTransform");
|
||||
Uniforms [(int)Uniform.Render_Transform_UV] = GL.GetUniformLocation (ProgramUV, "renderTransform");
|
||||
if (!CompileShader(ShaderType.FragmentShader, FragShaderUVSource, out fragShaderUV))
|
||||
{
|
||||
Console.Error.WriteLine("Failed to compile UV fragment shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
//Release vertex and fragment shaders.
|
||||
if (vertShader != 0) {
|
||||
GL.DetachShader (ProgramY, vertShader);
|
||||
GL.DetachShader (ProgramUV, vertShader);
|
||||
GL.DeleteShader (vertShader);
|
||||
}
|
||||
if (fragShaderY != 0) {
|
||||
GL.DetachShader (ProgramY, fragShaderY);
|
||||
GL.DeleteShader (fragShaderY);
|
||||
}
|
||||
if (fragShaderUV != 0) {
|
||||
GL.DetachShader (ProgramUV, fragShaderUV);
|
||||
GL.DeleteShader (fragShaderUV);
|
||||
}
|
||||
// Attach vertex shader to programY.
|
||||
GL.AttachShader(ProgramY, vertShader);
|
||||
|
||||
return true;
|
||||
}
|
||||
// Attach fragment shader to programY.
|
||||
GL.AttachShader(ProgramY, fragShaderY);
|
||||
|
||||
static bool CompileShader(ShaderType type, string sourceString, out int shader)
|
||||
{
|
||||
if (string.IsNullOrEmpty (sourceString)) {
|
||||
Console.Error.WriteLine ("Failed to load vertex shader: Empty source string");
|
||||
shader = 0;
|
||||
return false;
|
||||
}
|
||||
// Attach vertex shader to programUV.
|
||||
GL.AttachShader(ProgramUV, vertShader);
|
||||
|
||||
int status;
|
||||
shader = GL.CreateShader (type);
|
||||
GL.ShaderSource (shader, sourceString);
|
||||
GL.CompileShader (shader);
|
||||
// Attach fragment shader to programUV.
|
||||
GL.AttachShader(ProgramUV, fragShaderUV);
|
||||
|
||||
// Bind attribute locations. This needs to be done prior to linking.
|
||||
GL.BindAttribLocation(ProgramY, (int)Attrib.Vertex_Y, "position");
|
||||
GL.BindAttribLocation(ProgramY, (int)Attrib.TexCoord_Y, "texCoord");
|
||||
GL.BindAttribLocation(ProgramUV, (int)Attrib.Vertex_UV, "position");
|
||||
GL.BindAttribLocation(ProgramUV, (int)Attrib.TexCoord_UV, "texCoord");
|
||||
|
||||
// Link the program.
|
||||
if (!LinkProgram(ProgramY) || !LinkProgram(ProgramUV))
|
||||
{
|
||||
Console.Error.WriteLine("Failed to link program");
|
||||
if (vertShader != 0)
|
||||
{
|
||||
GL.DeleteShader(vertShader);
|
||||
vertShader = 0;
|
||||
}
|
||||
|
||||
if (fragShaderY != 0)
|
||||
{
|
||||
GL.DeleteShader(fragShaderY);
|
||||
fragShaderY = 0;
|
||||
}
|
||||
|
||||
if (fragShaderUV != 0)
|
||||
{
|
||||
GL.DeleteShader(fragShaderUV);
|
||||
fragShaderUV = 0;
|
||||
}
|
||||
|
||||
if (ProgramY != 0)
|
||||
{
|
||||
GL.DeleteProgram(ProgramY);
|
||||
ProgramY = 0;
|
||||
}
|
||||
|
||||
if (ProgramUV != 0)
|
||||
{
|
||||
GL.DeleteProgram(ProgramUV);
|
||||
ProgramUV = 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get uniform locations.
|
||||
Uniforms[(int)Uniform.Y] = GL.GetUniformLocation(ProgramY, "SamplerY");
|
||||
Uniforms[(int)Uniform.UV] = GL.GetUniformLocation(ProgramUV, "SamplerUV");
|
||||
Uniforms[(int)Uniform.Render_Transform_Y] = GL.GetUniformLocation(ProgramY, "renderTransform");
|
||||
Uniforms[(int)Uniform.Render_Transform_UV] = GL.GetUniformLocation(ProgramUV, "renderTransform");
|
||||
|
||||
// Release vertex and fragment shaders.
|
||||
if (vertShader != 0)
|
||||
{
|
||||
GL.DetachShader(ProgramY, vertShader);
|
||||
GL.DetachShader(ProgramUV, vertShader);
|
||||
GL.DeleteShader(vertShader);
|
||||
}
|
||||
|
||||
if (fragShaderY != 0)
|
||||
{
|
||||
GL.DetachShader(ProgramY, fragShaderY);
|
||||
GL.DeleteShader(fragShaderY);
|
||||
}
|
||||
|
||||
if (fragShaderUV != 0)
|
||||
{
|
||||
GL.DetachShader(ProgramUV, fragShaderUV);
|
||||
GL.DeleteShader(fragShaderUV);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool CompileShader(ShaderType type, string sourceString, out int shader)
|
||||
{
|
||||
if (string.IsNullOrEmpty(sourceString))
|
||||
{
|
||||
Console.Error.WriteLine("Failed to load vertex shader: Empty source string");
|
||||
shader = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
shader = GL.CreateShader(type);
|
||||
GL.ShaderSource(shader, sourceString);
|
||||
GL.CompileShader(shader);
|
||||
|
||||
#if DEBUG
|
||||
int logLength;
|
||||
GL.GetShader (shader, ShaderParameter.InfoLogLength, out logLength);
|
||||
if (logLength > 0) {
|
||||
var log = GL.GetShaderInfoLog (shader);
|
||||
Console.WriteLine ("Shader compile log: {0}", log);
|
||||
}
|
||||
GL.GetShader(shader, ShaderParameter.InfoLogLength, out int logLength);
|
||||
if (logLength > 0)
|
||||
{
|
||||
var log = GL.GetShaderInfoLog(shader);
|
||||
Console.WriteLine("Shader compile log: {0}", log);
|
||||
}
|
||||
#endif
|
||||
|
||||
GL.GetShader (shader, ShaderParameter.CompileStatus, out status);
|
||||
if (status == 0) {
|
||||
GL.DeleteShader (shader);
|
||||
return false;
|
||||
}
|
||||
GL.GetShader(shader, ShaderParameter.CompileStatus, out int status);
|
||||
if (status == 0)
|
||||
{
|
||||
GL.DeleteShader(shader);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool LinkProgram(int program)
|
||||
{
|
||||
int status;
|
||||
GL.LinkProgram (program);
|
||||
private static bool LinkProgram(int program)
|
||||
{
|
||||
GL.LinkProgram(program);
|
||||
|
||||
#if DEBUG
|
||||
int logLength;
|
||||
GL.GetProgram(program, ProgramParameter.InfoLogLength, out logLength);
|
||||
if (logLength > 0) {
|
||||
var log = new StringBuilder(logLength);
|
||||
GL.GetProgramInfoLog (program, logLength, out logLength, log);
|
||||
Console.WriteLine("Program link log: {0}", log);
|
||||
log.Clear ();
|
||||
}
|
||||
GL.GetProgram(program, ProgramParameter.InfoLogLength, out int logLength);
|
||||
if (logLength > 0)
|
||||
{
|
||||
var log = new StringBuilder(logLength);
|
||||
GL.GetProgramInfoLog(program, logLength, out logLength, log);
|
||||
Console.WriteLine($"Program link log: {log}");
|
||||
log.Clear();
|
||||
}
|
||||
#endif
|
||||
|
||||
GL.GetProgram (program, ProgramParameter.LinkStatus, out status);
|
||||
return status != 0;
|
||||
}
|
||||
GL.GetProgram(program, ProgramParameter.LinkStatus, out int status);
|
||||
return status != 0;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ValidateProgram(int program)
|
||||
{
|
||||
int logLength;
|
||||
int status;
|
||||
GL.ValidateProgram (program);
|
||||
#endregion
|
||||
|
||||
GL.GetProgram (program, ProgramParameter.InfoLogLength, out logLength);
|
||||
if (logLength > 0) {
|
||||
var log = new StringBuilder(logLength);
|
||||
GL.GetProgramInfoLog (program, logLength, out logLength, log);
|
||||
Console.WriteLine("Program validate log: {0}", log);
|
||||
log.Clear ();
|
||||
}
|
||||
|
||||
GL.GetProgram (program, ProgramParameter.ValidateStatus, out status);
|
||||
return status != 0;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Uniform
|
||||
{
|
||||
Y,
|
||||
UV,
|
||||
Render_Transform_Y,
|
||||
Render_Transform_UV,
|
||||
Num_Uniforms
|
||||
}
|
||||
|
||||
public enum Attrib{
|
||||
Vertex_Y,
|
||||
TexCoord_Y,
|
||||
Vertex_UV,
|
||||
TexCoord_UV,
|
||||
Num_Attributes
|
||||
}
|
||||
|
||||
}
|
||||
public enum Uniform
|
||||
{
|
||||
Y,
|
||||
UV,
|
||||
Render_Transform_Y,
|
||||
Render_Transform_UV,
|
||||
Num_Uniforms
|
||||
}
|
||||
|
||||
public enum Attrib
|
||||
{
|
||||
Vertex_Y,
|
||||
TexCoord_Y,
|
||||
Vertex_UV,
|
||||
TexCoord_UV,
|
||||
Num_Attributes
|
||||
}
|
||||
}
|
|
@ -1,34 +1,35 @@
|
|||
// This file has been autogenerated from a class added in the UI designer.
|
||||
|
||||
using System;
|
||||
|
||||
using AVFoundation;
|
||||
using Foundation;
|
||||
using UIKit;
|
||||
using AVFoundation;
|
||||
using ObjCRuntime;
|
||||
using System;
|
||||
using UIKit;
|
||||
|
||||
namespace AVCustomEdit
|
||||
{
|
||||
public partial class PlayerView : UIView
|
||||
{
|
||||
public AVPlayer player
|
||||
{
|
||||
get{
|
||||
return (this.Layer as AVPlayerLayer).Player;
|
||||
}
|
||||
set{
|
||||
(this.Layer as AVPlayerLayer).Player = value;
|
||||
}
|
||||
}
|
||||
[Register("PlayerView")]
|
||||
public class PlayerView : UIView
|
||||
{
|
||||
public PlayerView(IntPtr handle) : base(handle) { }
|
||||
|
||||
public PlayerView (IntPtr handle) : base (handle)
|
||||
{
|
||||
}
|
||||
public AVPlayer Player
|
||||
{
|
||||
get
|
||||
{
|
||||
return (this.Layer as AVPlayerLayer).Player;
|
||||
}
|
||||
set
|
||||
{
|
||||
(this.Layer as AVPlayerLayer).Player = value;
|
||||
}
|
||||
}
|
||||
|
||||
[Export ("layerClass")]
|
||||
public static Class LayerClass()
|
||||
{
|
||||
return new Class (typeof(AVPlayerLayer));
|
||||
}
|
||||
}
|
||||
public static Class LayerClass
|
||||
{
|
||||
[Export("layerClass")]
|
||||
get
|
||||
{
|
||||
return new Class(typeof(AVPlayerLayer));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
// WARNING
|
||||
//
|
||||
// This file has been generated automatically by Xamarin Studio to store outlets and
|
||||
// actions made in the UI designer. If it is removed, they will be lost.
|
||||
// Manual changes to this file may not be handled correctly.
|
||||
//
|
||||
using Foundation;
|
||||
using System.CodeDom.Compiler;
|
||||
|
||||
namespace AVCustomEdit
|
||||
{
|
||||
[Register ("PlayerView")]
|
||||
partial class PlayerView
|
||||
{
|
||||
|
||||
void ReleaseDesignerOutlets ()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
// WARNING
|
||||
//
|
||||
// This file has been generated automatically by Visual Studio from the outlets and
|
||||
// actions declared in your storyboard file.
|
||||
// Manual changes to this file will not be maintained.
|
||||
//
|
||||
using Foundation;
|
||||
using System;
|
||||
using System.CodeDom.Compiler;
|
||||
|
||||
namespace AVCustomEdit
|
||||
{
|
||||
[Register ("PlayerView")]
|
||||
partial class PlayerView
|
||||
{
|
||||
void ReleaseDesignerOutlets ()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,553 +0,0 @@
|
|||
// This file has been autogenerated from a class added in the UI designer.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Foundation;
|
||||
using UIKit;
|
||||
using AVFoundation;
|
||||
using CoreFoundation;
|
||||
using CoreMedia;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
using AssetsLibrary;
|
||||
|
||||
namespace AVCustomEdit
|
||||
{
|
||||
public partial class PlayerViewController : UIViewController
|
||||
{
|
||||
const int NSEC_PER_SEC = 1000000000;
|
||||
|
||||
Boolean playing;
|
||||
Boolean scrubInFlight;
|
||||
Boolean seekToZeroBeforePlaying;
|
||||
float lastScrubSliderValue;
|
||||
float playRateToRestore;
|
||||
NSObject timeObserver;
|
||||
|
||||
float transitionDuration;
|
||||
int transitionType;
|
||||
bool transitionsEnabled;
|
||||
|
||||
NSObject observer;
|
||||
|
||||
NSTimer progressTimer;
|
||||
|
||||
public SimpleEditor Editor;
|
||||
public List<AVAsset> Clips;
|
||||
public List<NSValue> ClipTimeRanges;
|
||||
|
||||
public AVPlayer Player;
|
||||
public AVPlayerItem PlayerItem;
|
||||
public UIPopoverController Popover;
|
||||
|
||||
public static NSString StatusObservationContext = new NSString("AVCustomEditPlayerViewControllerStatusObservationContext");
|
||||
public static NSString RateObservationContext = new NSString ("AVCustomEditPlayerViewControllerRateObservationContext");
|
||||
|
||||
double playerItemDuration {
|
||||
get {
|
||||
if (Player == null)
|
||||
return Double.PositiveInfinity;
|
||||
|
||||
CMTime itemDuration = CMTime.Invalid;
|
||||
AVPlayerItem playerItem = Player.CurrentItem;
|
||||
|
||||
if (Player.Status == AVPlayerStatus.ReadyToPlay) {
|
||||
itemDuration = playerItem.Duration;
|
||||
}
|
||||
|
||||
return itemDuration.Seconds;
|
||||
}
|
||||
}
|
||||
|
||||
public PlayerViewController (IntPtr handle) : base (handle)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void ViewDidLoad ()
|
||||
{
|
||||
base.ViewDidLoad ();
|
||||
|
||||
gestureRecognizer.ShouldReceiveTouch = ShouldReceiveTouch;
|
||||
gestureRecognizer.AddTarget (() => HandleTapGesture (gestureRecognizer));
|
||||
|
||||
Editor = new SimpleEditor ();
|
||||
|
||||
Clips = new List<AVAsset> ();
|
||||
ClipTimeRanges = new List<NSValue> ();
|
||||
|
||||
transitionType = TransitionTypeController.DiagonalWipeTransition;
|
||||
transitionDuration = 2.0f;
|
||||
transitionsEnabled = true;
|
||||
|
||||
updateScrubber ();
|
||||
updateTimeLabel ();
|
||||
|
||||
setupEditingAndPlayback ();
|
||||
}
|
||||
|
||||
public override void ViewDidAppear (bool animated)
|
||||
{
|
||||
base.ViewDidAppear (animated);
|
||||
|
||||
if (Player == null) {
|
||||
seekToZeroBeforePlaying = false;
|
||||
Player = new AVPlayer ();
|
||||
Player.AddObserver (this, (NSString)"rate", NSKeyValueObservingOptions.Old | NSKeyValueObservingOptions.New, RateObservationContext.Handle);
|
||||
|
||||
playerView.player = Player;
|
||||
}
|
||||
|
||||
addTimeObserverToPlayer ();
|
||||
|
||||
// Build AVComposition and AVVideoComposition objects for playback
|
||||
Editor.BuildCompositionObjects (true);
|
||||
synchronizePlayerWithEditor ();
|
||||
|
||||
}
|
||||
|
||||
public override void ViewWillDisappear (bool animated)
|
||||
{
|
||||
base.ViewWillDisappear (animated);
|
||||
Player.Pause();
|
||||
removeTimeObserverFromPlayer ();
|
||||
}
|
||||
|
||||
public override void PrepareForSegue (UIStoryboardSegue segue, NSObject sender)
|
||||
{
|
||||
if (segue.Identifier == "Transition") {
|
||||
var transitionController = (segue.DestinationViewController as UINavigationController).TopViewController as TransitionTypeController;
|
||||
if (UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad)
|
||||
Popover = (segue as UIStoryboardPopoverSegue).PopoverController;
|
||||
|
||||
transitionController.TransitionTypePicked = TransitionTypePicked;
|
||||
transitionController.CurrentTransition = transitionType;
|
||||
|
||||
if (transitionType == TransitionTypeController.CrossDissolveTransition) {
|
||||
if (transitionController.CrossDissolveCell == null)
|
||||
transitionController.LoadView ();
|
||||
transitionController.CrossDissolveCell.Accessory = UITableViewCellAccessory.Checkmark;
|
||||
} else {
|
||||
if (transitionController.DiagonalWipeCell == null)
|
||||
transitionController.LoadView ();
|
||||
transitionController.DiagonalWipeCell.Accessory = UITableViewCellAccessory.Checkmark;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Simple Editor
|
||||
void setupEditingAndPlayback()
|
||||
{
|
||||
var path1 = NSBundle.MainBundle.PathForResource ("sample_clip1", "m4v");
|
||||
var path2 = NSBundle.MainBundle.PathForResource ("sample_clip2", "mov");
|
||||
var asset1 = AVAsset.FromUrl (new NSUrl (path1, false)) as AVUrlAsset;
|
||||
var asset2 = AVAsset.FromUrl (new NSUrl (path2, false)) as AVUrlAsset;
|
||||
|
||||
DispatchGroup dispatchGroup = DispatchGroup.Create ();
|
||||
|
||||
string[] assetKeys = {
|
||||
"tracks",
|
||||
"duration",
|
||||
"composable"
|
||||
};
|
||||
|
||||
loadAsset (asset1, assetKeys, dispatchGroup);
|
||||
loadAsset (asset2, assetKeys, dispatchGroup);
|
||||
|
||||
// Wait until both assets are loaded
|
||||
dispatchGroup.Wait (DispatchTime.Forever);
|
||||
InvokeOnMainThread(delegate{
|
||||
synchronizeWithEditor();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void loadAsset(AVAsset asset, string[] assetKeysToLoad, DispatchGroup dispatchGroup)
|
||||
{
|
||||
dispatchGroup.Enter ();
|
||||
asset.LoadValuesAsynchronously (assetKeysToLoad, () => {
|
||||
foreach(var key in assetKeysToLoad)
|
||||
{
|
||||
NSError error;
|
||||
if(asset.StatusOfValue(key, out error) == AVKeyValueStatus.Failed)
|
||||
{
|
||||
Console.Error.WriteLine("Key value loading failed for key {0} with error: {1}", key, error);
|
||||
dispatchGroup.Leave();
|
||||
}
|
||||
|
||||
}
|
||||
if(!asset.Composable)
|
||||
{
|
||||
Console.Error.WriteLine("Asset is not composable");
|
||||
dispatchGroup.Leave();
|
||||
}
|
||||
Clips.Add(asset);
|
||||
ClipTimeRanges.Add(NSValue.FromCMTimeRange(new CMTimeRange {
|
||||
Start = CMTime.FromSeconds(0, 1),
|
||||
Duration = CMTime.FromSeconds(5,1)
|
||||
}));
|
||||
dispatchGroup.Leave();
|
||||
});
|
||||
}
|
||||
|
||||
void synchronizePlayerWithEditor()
|
||||
{
|
||||
if (Player == null)
|
||||
return;
|
||||
|
||||
AVPlayerItem playerItem = Editor.PlayerItem;
|
||||
var status = (NSString)"status";
|
||||
|
||||
if (PlayerItem != playerItem) {
|
||||
if (PlayerItem != null) {
|
||||
PlayerItem.RemoveObserver (this, status);
|
||||
NSNotificationCenter.DefaultCenter.RemoveObserver (observer, AVPlayerItem.DidPlayToEndTimeNotification, PlayerItem);
|
||||
}
|
||||
|
||||
PlayerItem = playerItem;
|
||||
|
||||
if (PlayerItem != null) {
|
||||
PlayerItem.SeekingWaitsForVideoCompositionRendering = true;
|
||||
PlayerItem.AddObserver (this, status, NSKeyValueObservingOptions.New|
|
||||
NSKeyValueObservingOptions.Initial, StatusObservationContext.Handle);
|
||||
observer = NSNotificationCenter.DefaultCenter.AddObserver (AVPlayerItem.DidPlayToEndTimeNotification, notification => {
|
||||
Console.WriteLine ("Seek Zero = true");
|
||||
seekToZeroBeforePlaying = true;
|
||||
}, playerItem);
|
||||
//NSNotificationCenter.DefaultCenter.AddObserver (this, new MonoTouch.ObjCRuntime.Selector ("playerItemEnded:"), AVPlayerItem.DidPlayToEndTimeNotification, playerItem);
|
||||
}
|
||||
|
||||
Player.ReplaceCurrentItemWithPlayerItem (playerItem);
|
||||
}
|
||||
}
|
||||
|
||||
[Export("playerItemEnded:")]
|
||||
void playerItemEnded (NSObject obj)
|
||||
{
|
||||
Console.WriteLine("Seek Zero = true");
|
||||
seekToZeroBeforePlaying = true;
|
||||
}
|
||||
|
||||
void synchronizeWithEditor()
|
||||
{
|
||||
//Clips
|
||||
synchronizeEditorClips ();
|
||||
synchronizeEditorClipTimeRanges ();
|
||||
|
||||
//Transitions
|
||||
if (transitionsEnabled) {
|
||||
Editor.TransitionDuration = CMTime.FromSeconds (transitionDuration, 600);
|
||||
Editor.TransitionType = transitionType;
|
||||
|
||||
} else {
|
||||
Editor.TransitionDuration = CMTime.Invalid;
|
||||
}
|
||||
|
||||
Editor.BuildCompositionObjects (true);
|
||||
synchronizePlayerWithEditor ();
|
||||
}
|
||||
|
||||
void synchronizeEditorClips()
|
||||
{
|
||||
var validClips = new List<AVAsset> ();
|
||||
foreach (var asset in Clips) {
|
||||
if (asset != null)
|
||||
validClips.Add (asset);
|
||||
}
|
||||
|
||||
Editor.Clips = validClips;
|
||||
}
|
||||
|
||||
void synchronizeEditorClipTimeRanges()
|
||||
{
|
||||
var validClipTimeRanges = new List<NSValue> ();
|
||||
foreach (var timeRange in ClipTimeRanges) {
|
||||
if (timeRange != null)
|
||||
validClipTimeRanges.Add (timeRange);
|
||||
}
|
||||
|
||||
Editor.ClipTimeRanges = validClipTimeRanges;
|
||||
}
|
||||
|
||||
//Utilities
|
||||
void addTimeObserverToPlayer()
|
||||
{
|
||||
if (timeObserver != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Player == null)
|
||||
return;
|
||||
|
||||
if (Player.CurrentItem == null)
|
||||
return;
|
||||
|
||||
if (Player.CurrentItem.Status != AVPlayerItemStatus.ReadyToPlay)
|
||||
return;
|
||||
|
||||
double duration = playerItemDuration;
|
||||
|
||||
if (!Double.IsInfinity (duration)) {
|
||||
float width = (float)scrubber.Bounds.Width;
|
||||
double interval = 0.5 * duration / width;
|
||||
|
||||
if (interval > 1.0)
|
||||
interval = 1.0;
|
||||
timeObserver = Player.AddPeriodicTimeObserver (CMTime.FromSeconds (interval, NSEC_PER_SEC), DispatchQueue.MainQueue, delegate {
|
||||
updateScrubber();
|
||||
updateTimeLabel();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void removeTimeObserverFromPlayer()
|
||||
{
|
||||
if (timeObserver != null) {
|
||||
Player.RemoveTimeObserver (timeObserver);
|
||||
timeObserver = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void ObserveValue (NSString keyPath, NSObject ofObject, NSDictionary change, IntPtr context)
|
||||
{
|
||||
var ch = new NSObservedChange (change);
|
||||
if (context == RateObservationContext.Handle) {
|
||||
//TODO: need debug here.
|
||||
float newRate = ((NSNumber)ch.NewValue).FloatValue;
|
||||
var oldRateNum = (NSNumber)ch.OldValue;
|
||||
if (oldRateNum != null && newRate != oldRateNum.FloatValue) {
|
||||
playing = (newRate != 0.0f || playRateToRestore != 0.0f);
|
||||
updatePlayPauseButton ();
|
||||
updateScrubber ();
|
||||
updateTimeLabel ();
|
||||
}
|
||||
} else if (context == StatusObservationContext.Handle) {
|
||||
var playerItem = ofObject as AVPlayerItem;
|
||||
if (playerItem.Status == AVPlayerItemStatus.ReadyToPlay) {
|
||||
/* Once the AVPlayerItem becomes ready to play, i.e.
|
||||
[playerItem status] == AVPlayerItemStatusReadyToPlay,
|
||||
its duration can be fetched from the item. */
|
||||
addTimeObserverToPlayer ();
|
||||
} else if (playerItem.Status == AVPlayerItemStatus.Failed) {
|
||||
reportError (playerItem.Error);
|
||||
}
|
||||
} else {
|
||||
base.ObserveValue (keyPath, ofObject, change, context);
|
||||
}
|
||||
}
|
||||
|
||||
void updatePlayPauseButton()
|
||||
{
|
||||
var style = playing ? UIBarButtonSystemItem.Pause : UIBarButtonSystemItem.Play;
|
||||
var newPlayPauseButton = new UIBarButtonItem (style, (s, e) => togglePlayPause (s as UIBarButtonItem));
|
||||
|
||||
var items = toolBar.Items;
|
||||
items [0] = newPlayPauseButton;
|
||||
toolBar.SetItems (items, false);
|
||||
|
||||
playPauseButton = newPlayPauseButton;
|
||||
}
|
||||
|
||||
void updateTimeLabel()
|
||||
{
|
||||
if (Player == null)
|
||||
return;
|
||||
|
||||
var seconds = Player.CurrentTime.Seconds;
|
||||
if (double.IsInfinity(seconds))
|
||||
seconds = 0;
|
||||
|
||||
int secondsInt =(int) Math.Round (seconds);
|
||||
int minutes = secondsInt / 60;
|
||||
secondsInt -= minutes * 60;
|
||||
|
||||
currentTimeLabel.TextColor = UIColor.White;
|
||||
currentTimeLabel.TextAlignment = UITextAlignment.Center;
|
||||
currentTimeLabel.Text = string.Format ("{0} : {1}", minutes, secondsInt);
|
||||
|
||||
}
|
||||
|
||||
void updateScrubber()
|
||||
{
|
||||
double duration = playerItemDuration;
|
||||
if (!double.IsInfinity(duration)) {
|
||||
double time = Player.CurrentTime.Seconds;
|
||||
scrubber.Value = (float)(time / duration);
|
||||
} else {
|
||||
scrubber.Value = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void updateProgress(NSTimer timer)
|
||||
{
|
||||
var session = timer.UserInfo as AVAssetExportSession;
|
||||
if (session != null && session.Status == AVAssetExportSessionStatus.Exporting)
|
||||
exportProgressView.Progress = session.Progress;
|
||||
}
|
||||
|
||||
void reportError(NSError error)
|
||||
{
|
||||
DispatchQueue.MainQueue.DispatchAsync (() => {
|
||||
if (error == null)
|
||||
return;
|
||||
|
||||
new UIAlertView (error.LocalizedDescription, error.DebugDescription, null, "OK", null).Show ();
|
||||
});
|
||||
}
|
||||
|
||||
partial void togglePlayPause (UIBarButtonItem sender)
|
||||
{
|
||||
playing = !playing;
|
||||
if (playing) {
|
||||
if (seekToZeroBeforePlaying) {
|
||||
Player.Seek (CMTime.Zero);
|
||||
seekToZeroBeforePlaying = false;
|
||||
// player.Seek (CMTime.Zero, delegate(bool finished) {
|
||||
// if(finished){
|
||||
// Console.WriteLine ("Seeked to Zero");
|
||||
// this.seekToZeroBeforePlaying = false;
|
||||
// this.player.Play();
|
||||
// }
|
||||
// else
|
||||
// Console.WriteLine ("Seek to Zero Failed");
|
||||
// });
|
||||
}
|
||||
Player.Play();
|
||||
} else {
|
||||
Player.Pause();
|
||||
}
|
||||
}
|
||||
|
||||
partial void beginScrubbing (UISlider sender)
|
||||
{
|
||||
seekToZeroBeforePlaying = false;
|
||||
playRateToRestore = Player.Rate;
|
||||
Player.Rate = 0f;
|
||||
|
||||
removeTimeObserverFromPlayer ();
|
||||
}
|
||||
|
||||
async partial void endScrubbing (UISlider sender)
|
||||
{
|
||||
if (scrubInFlight)
|
||||
await scrubTo (lastScrubSliderValue);
|
||||
addTimeObserverToPlayer ();
|
||||
|
||||
Player.Rate = playRateToRestore;
|
||||
playRateToRestore = 0f;
|
||||
}
|
||||
|
||||
async partial void scrub (UISlider sender)
|
||||
{
|
||||
lastScrubSliderValue = scrubber.Value;
|
||||
|
||||
if (!scrubInFlight)
|
||||
await scrubTo (lastScrubSliderValue);
|
||||
}
|
||||
|
||||
async Task scrubTo (float sliderValue)
|
||||
{
|
||||
var duration = playerItemDuration;
|
||||
|
||||
if (Double.IsInfinity (duration))
|
||||
return;
|
||||
|
||||
var width = scrubber.Bounds.Width;
|
||||
|
||||
var time = duration * sliderValue;
|
||||
var tolerance = 1f * duration / width;
|
||||
|
||||
scrubInFlight = true;
|
||||
|
||||
await Player.SeekAsync (CMTime.FromSeconds (time, NSEC_PER_SEC), CMTime.FromSeconds (tolerance, NSEC_PER_SEC), CMTime.FromSeconds (tolerance, NSEC_PER_SEC));
|
||||
|
||||
scrubInFlight = false;
|
||||
updateTimeLabel ();
|
||||
}
|
||||
|
||||
void HandleTapGesture (UITapGestureRecognizer tapGestureRecognizer)
|
||||
{
|
||||
toolBar.Hidden = !toolBar.Hidden;
|
||||
currentTimeLabel.Hidden = !currentTimeLabel.Hidden;
|
||||
}
|
||||
|
||||
partial void exportToMovie (UIBarButtonItem sender)
|
||||
{
|
||||
exportProgressView.Hidden = false;
|
||||
|
||||
Player.Pause ();
|
||||
playPauseButton.Enabled = false;
|
||||
transitionButton.Enabled = false;
|
||||
scrubber.Enabled = false;
|
||||
exportButton.Enabled = false;
|
||||
|
||||
Editor.BuildCompositionObjects (false);
|
||||
|
||||
var session = Editor.AssetExportSession (AVAssetExportSession.PresetMediumQuality);
|
||||
|
||||
var filePath = Path.Combine (Path.GetTempPath(), "ExportedProject.mov");
|
||||
if (File.Exists (filePath))
|
||||
File.Delete (filePath);
|
||||
|
||||
session.OutputUrl = NSUrl.FromFilename (filePath);
|
||||
session.OutputFileType = AVFileType.QuickTimeMovie;
|
||||
|
||||
session.ExportAsynchronously (() => DispatchQueue.MainQueue.DispatchAsync (() => exportCompleted (session)));
|
||||
|
||||
progressTimer = NSTimer.CreateRepeatingTimer (0.5, d => updateProgress (progressTimer));
|
||||
NSRunLoop.Current.AddTimer (progressTimer, NSRunLoopMode.Default);
|
||||
}
|
||||
|
||||
void exportCompleted (AVAssetExportSession session)
|
||||
{
|
||||
exportProgressView.Hidden = true;
|
||||
currentTimeLabel.Hidden = false;
|
||||
var outputUrl = session.OutputUrl;
|
||||
|
||||
progressTimer.Invalidate ();
|
||||
progressTimer = null;
|
||||
|
||||
if (session.Status != AVAssetExportSessionStatus.Completed) {
|
||||
Console.WriteLine ("exportSession error:{0}", session.Error.LocalizedDescription);
|
||||
reportError (session.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
exportProgressView.Progress = 1f;
|
||||
|
||||
var library = new ALAssetsLibrary ();
|
||||
library.WriteVideoToSavedPhotosAlbum (outputUrl, (assetURL, error) => {
|
||||
if (error != null) {
|
||||
Console.WriteLine ("writeVideoToAssetsLibrary failed: {0}", error.LocalizedDescription);
|
||||
reportError (error);
|
||||
}
|
||||
library.Dispose();
|
||||
});
|
||||
|
||||
Player.Play ();
|
||||
playPauseButton.Enabled = true;
|
||||
transitionButton.Enabled = true;
|
||||
scrubber.Enabled = true;
|
||||
exportButton.Enabled = true;
|
||||
}
|
||||
|
||||
bool ShouldReceiveTouch (UIGestureRecognizer gestureRecognizer, UITouch touch)
|
||||
{
|
||||
return touch.View == View;
|
||||
}
|
||||
|
||||
public void TransitionTypePicked (int transitionType)
|
||||
{
|
||||
this.transitionType = transitionType;
|
||||
|
||||
synchronizeWithEditor ();
|
||||
|
||||
if (UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad)
|
||||
Popover.Dismiss (true);
|
||||
else
|
||||
DismissViewController (true, null);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
// WARNING
|
||||
//
|
||||
// This file has been generated automatically by Xamarin Studio from the outlets and
|
||||
// actions declared in your storyboard file.
|
||||
// Manual changes to this file will not be maintained.
|
||||
//
|
||||
using Foundation;
|
||||
using UIKit;
|
||||
using System.CodeDom.Compiler;
|
||||
|
||||
namespace AVCustomEdit
|
||||
{
|
||||
[Register ("PlayerViewController")]
|
||||
partial class PlayerViewController
|
||||
{
|
||||
[Outlet]
|
||||
[GeneratedCodeAttribute ("iOS Designer", "1.0")]
|
||||
UIKit.UILabel currentTimeLabel { get; set; }
|
||||
|
||||
[Outlet]
|
||||
[GeneratedCodeAttribute ("iOS Designer", "1.0")]
|
||||
UIKit.UIBarButtonItem exportButton { get; set; }
|
||||
|
||||
[Outlet]
|
||||
[GeneratedCodeAttribute ("iOS Designer", "1.0")]
|
||||
UIKit.UIProgressView exportProgressView { get; set; }
|
||||
|
||||
[Outlet]
|
||||
[GeneratedCodeAttribute ("iOS Designer", "1.0")]
|
||||
UIKit.UITapGestureRecognizer gestureRecognizer { get; set; }
|
||||
|
||||
[Outlet]
|
||||
[GeneratedCodeAttribute ("iOS Designer", "1.0")]
|
||||
AVCustomEdit.PlayerView playerView { get; set; }
|
||||
|
||||
[Outlet]
|
||||
[GeneratedCodeAttribute ("iOS Designer", "1.0")]
|
||||
UIKit.UIBarButtonItem playPauseButton { get; set; }
|
||||
|
||||
[Outlet]
|
||||
[GeneratedCodeAttribute ("iOS Designer", "1.0")]
|
||||
UIKit.UISlider scrubber { get; set; }
|
||||
|
||||
[Outlet]
|
||||
[GeneratedCodeAttribute ("iOS Designer", "1.0")]
|
||||
UIKit.UIToolbar toolBar { get; set; }
|
||||
|
||||
[Outlet]
|
||||
[GeneratedCodeAttribute ("iOS Designer", "1.0")]
|
||||
UIKit.UIBarButtonItem transitionButton { get; set; }
|
||||
|
||||
[Action ("beginScrubbing:")]
|
||||
[GeneratedCodeAttribute ("iOS Designer", "1.0")]
|
||||
partial void beginScrubbing (UISlider sender);
|
||||
|
||||
[Action ("endScrubbing:")]
|
||||
[GeneratedCodeAttribute ("iOS Designer", "1.0")]
|
||||
partial void endScrubbing (UISlider sender);
|
||||
|
||||
[Action ("exportToMovie:")]
|
||||
[GeneratedCodeAttribute ("iOS Designer", "1.0")]
|
||||
partial void exportToMovie (UIBarButtonItem sender);
|
||||
|
||||
[Action ("scrub:")]
|
||||
[GeneratedCodeAttribute ("iOS Designer", "1.0")]
|
||||
partial void scrub (UISlider sender);
|
||||
|
||||
[Action ("togglePlayPause:")]
|
||||
[GeneratedCodeAttribute ("iOS Designer", "1.0")]
|
||||
partial void togglePlayPause (UIBarButtonItem sender);
|
||||
|
||||
void ReleaseDesignerOutlets ()
|
||||
{
|
||||
if (currentTimeLabel != null) {
|
||||
currentTimeLabel.Dispose ();
|
||||
currentTimeLabel = null;
|
||||
}
|
||||
if (exportButton != null) {
|
||||
exportButton.Dispose ();
|
||||
exportButton = null;
|
||||
}
|
||||
if (exportProgressView != null) {
|
||||
exportProgressView.Dispose ();
|
||||
exportProgressView = null;
|
||||
}
|
||||
if (gestureRecognizer != null) {
|
||||
gestureRecognizer.Dispose ();
|
||||
gestureRecognizer = null;
|
||||
}
|
||||
if (playerView != null) {
|
||||
playerView.Dispose ();
|
||||
playerView = null;
|
||||
}
|
||||
if (playPauseButton != null) {
|
||||
playPauseButton.Dispose ();
|
||||
playPauseButton = null;
|
||||
}
|
||||
if (scrubber != null) {
|
||||
scrubber.Dispose ();
|
||||
scrubber = null;
|
||||
}
|
||||
if (toolBar != null) {
|
||||
toolBar.Dispose ();
|
||||
toolBar = null;
|
||||
}
|
||||
if (transitionButton != null) {
|
||||
transitionButton.Dispose ();
|
||||
transitionButton = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Двоичные данные
AVCustomEdit/AVCustomEdit/Resources/Default-568h@2x.png
До Ширина: | Высота: | Размер: 12 KiB |
Двоичные данные
AVCustomEdit/AVCustomEdit/Resources/Default.png
До Ширина: | Высота: | Размер: 5.2 KiB |
Двоичные данные
AVCustomEdit/AVCustomEdit/Resources/Default@2x.png
До Ширина: | Высота: | Размер: 12 KiB |
Двоичные данные
AVCustomEdit/AVCustomEdit/Resources/Icon-72.png
До Ширина: | Высота: | Размер: 6.6 KiB |
Двоичные данные
AVCustomEdit/AVCustomEdit/Resources/Icon-72@2x.png
До Ширина: | Высота: | Размер: 18 KiB |
Двоичные данные
AVCustomEdit/AVCustomEdit/Resources/Icon-Small-50.png
До Ширина: | Высота: | Размер: 4.2 KiB |
Двоичные данные
AVCustomEdit/AVCustomEdit/Resources/Icon-Small.png
До Ширина: | Высота: | Размер: 2.0 KiB |
Двоичные данные
AVCustomEdit/AVCustomEdit/Resources/Icon-Small@2x.png
До Ширина: | Высота: | Размер: 5.2 KiB |
Двоичные данные
AVCustomEdit/AVCustomEdit/Resources/Icon.png
До Ширина: | Высота: | Размер: 5.1 KiB |
Двоичные данные
AVCustomEdit/AVCustomEdit/Resources/Icon@2x.png
До Ширина: | Высота: | Размер: 14 KiB |
|
@ -1,252 +1,212 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Foundation;
|
||||
using CoreMedia;
|
||||
using AVFoundation;
|
||||
using CoreMedia;
|
||||
using Foundation;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AVCustomEdit
|
||||
{
|
||||
public class SimpleEditor : NSObject
|
||||
{
|
||||
public List<AVAsset> Clips;
|
||||
public List<NSValue> ClipTimeRanges;
|
||||
public class SimpleEditor : NSObject
|
||||
{
|
||||
private AVMutableComposition composition;
|
||||
|
||||
public int TransitionType;
|
||||
public CMTime TransitionDuration;
|
||||
AVMutableComposition Composition;
|
||||
AVMutableVideoComposition VideoComposition;
|
||||
private AVMutableVideoComposition videoComposition;
|
||||
|
||||
void buildTransitionComposition(AVMutableComposition composition, AVMutableVideoComposition videoComposition)
|
||||
{
|
||||
CMTime nextClipStartTime = CMTime.Zero;
|
||||
int clipsCount = Clips.Count;
|
||||
public List<AVAsset> Clips { get; set; } // array of AVURLAssets
|
||||
|
||||
// Make transitionDuration no greater than half the shortest clip duration.
|
||||
CMTime transitionDuration = TransitionDuration;
|
||||
public List<NSValue> ClipTimeRanges { get; set; } // array of CMTimeRanges stored in NSValues.
|
||||
|
||||
foreach (var clipTimeRange in ClipTimeRanges) {
|
||||
if (clipTimeRange == null)
|
||||
continue;
|
||||
public TransitionType TransitionType { get; set; }
|
||||
|
||||
CMTime halfClipDuration = clipTimeRange.CMTimeRangeValue.Duration;
|
||||
halfClipDuration.TimeScale *= 2;
|
||||
transitionDuration = CMTime.GetMinimum(transitionDuration,halfClipDuration);
|
||||
}
|
||||
public CMTime TransitionDuration { get; set; }
|
||||
|
||||
// Add two video tracks and two audio tracks.
|
||||
var compositionVideoTracks = new AVMutableCompositionTrack [2];
|
||||
var compositionAudioTracks = new AVMutableCompositionTrack [2];
|
||||
public AVPlayerItem PlayerItem => new AVPlayerItem(this.composition) { VideoComposition = this.videoComposition };
|
||||
|
||||
compositionVideoTracks [0] = composition.AddMutableTrack (AVMediaType.Video, 0);
|
||||
compositionVideoTracks [1] = composition.AddMutableTrack (AVMediaType.Video, 0);
|
||||
compositionAudioTracks [0] = composition.AddMutableTrack (AVMediaType.Audio, 0);
|
||||
compositionAudioTracks [1] = composition.AddMutableTrack (AVMediaType.Audio, 0);
|
||||
private void BuildTransitionComposition(AVMutableComposition mutableComposition, AVMutableVideoComposition mutableVideoComposition)
|
||||
{
|
||||
var nextClipStartTime = CMTime.Zero;
|
||||
var clipsCount = this.Clips.Count;
|
||||
|
||||
var passThroughTimeRanges = new CMTimeRange[clipsCount];
|
||||
var transitionTimeRanges = new CMTimeRange[clipsCount];
|
||||
// Make transitionDuration no greater than half the shortest clip duration.
|
||||
var transitionDuration = this.TransitionDuration;
|
||||
foreach (var clipTimeRange in this.ClipTimeRanges)
|
||||
{
|
||||
if (clipTimeRange != null)
|
||||
{
|
||||
var halfClipDuration = clipTimeRange.CMTimeRangeValue.Duration;
|
||||
halfClipDuration.TimeScale *= 2; // You can halve a rational by doubling its denominator.
|
||||
transitionDuration = CMTime.GetMinimum(transitionDuration, halfClipDuration);
|
||||
}
|
||||
}
|
||||
|
||||
// Place clips into alternating video & audio tracks in composition, overlapped by transitionDuration.
|
||||
for(int i = 0; i < clipsCount; i++)
|
||||
{
|
||||
int alternatingIndex = i % 2;
|
||||
AVAsset asset = Clips [i];
|
||||
NSValue clipTimeRange = ClipTimeRanges [i];
|
||||
CMTimeRange timeRangeInAsset;
|
||||
if (clipTimeRange != null)
|
||||
timeRangeInAsset = clipTimeRange.CMTimeRangeValue;
|
||||
else
|
||||
{
|
||||
timeRangeInAsset = new CMTimeRange {
|
||||
Start = CMTime.Zero,
|
||||
Duration = asset.Duration
|
||||
};
|
||||
}
|
||||
NSError error;
|
||||
AVAssetTrack clipVideoTrack = asset.TracksWithMediaType (AVMediaType.Video) [0];
|
||||
compositionVideoTracks [alternatingIndex].InsertTimeRange (timeRangeInAsset, clipVideoTrack, nextClipStartTime, out error);
|
||||
// Add two video tracks and two audio tracks.
|
||||
var compositionVideoTracks = new AVMutableCompositionTrack[2];
|
||||
var compositionAudioTracks = new AVMutableCompositionTrack[2];
|
||||
|
||||
AVAssetTrack clipAudioTrack = asset.TracksWithMediaType (AVMediaType.Audio) [0];
|
||||
compositionAudioTracks [alternatingIndex].InsertTimeRange (timeRangeInAsset, clipAudioTrack, nextClipStartTime, out error);
|
||||
compositionVideoTracks[0] = mutableComposition.AddMutableTrack(AVMediaType.Video, 0);
|
||||
compositionVideoTracks[1] = mutableComposition.AddMutableTrack(AVMediaType.Video, 0);
|
||||
compositionAudioTracks[0] = mutableComposition.AddMutableTrack(AVMediaType.Audio, 0);
|
||||
compositionAudioTracks[1] = mutableComposition.AddMutableTrack(AVMediaType.Audio, 0);
|
||||
|
||||
// Remember the time range in which this clip should pass through.
|
||||
// First clip ends with a transition.
|
||||
// Second clip begins with a transition.
|
||||
// Exclude that transition from the pass through time ranges
|
||||
passThroughTimeRanges [i] = new CMTimeRange {
|
||||
Start = nextClipStartTime,
|
||||
Duration = timeRangeInAsset.Duration
|
||||
};
|
||||
var passThroughTimeRanges = new CMTimeRange[clipsCount];
|
||||
var transitionTimeRanges = new CMTimeRange[clipsCount];
|
||||
|
||||
if (i > 0) {
|
||||
passThroughTimeRanges[i].Start = CMTime.Add(passThroughTimeRanges[i].Start, transitionDuration);
|
||||
passThroughTimeRanges[i].Duration = CMTime.Subtract(passThroughTimeRanges[i].Duration, transitionDuration);
|
||||
}
|
||||
if( i + 1 < clipsCount)
|
||||
passThroughTimeRanges[i].Duration = CMTime.Subtract(passThroughTimeRanges[i].Duration,transitionDuration);
|
||||
// Place clips into alternating video & audio tracks in composition, overlapped by transitionDuration.
|
||||
for (int i = 0; i < clipsCount; i++)
|
||||
{
|
||||
int alternatingIndex = i % 2; // alternating targets: 0, 1, 0, 1, ...
|
||||
var asset = this.Clips[i];
|
||||
var clipTimeRange = this.ClipTimeRanges[i];
|
||||
|
||||
// The end of this clip will overlap the start of the next by transitionDuration.
|
||||
// (Note: this arithmetic falls apart if timeRangeInAsset.duration < 2 * transitionDuration.)
|
||||
nextClipStartTime = CMTime.Add (nextClipStartTime, timeRangeInAsset.Duration);
|
||||
nextClipStartTime = CMTime.Subtract (nextClipStartTime, transitionDuration);
|
||||
var timeRangeInAsset = clipTimeRange != null ? clipTimeRange.CMTimeRangeValue
|
||||
: new CMTimeRange { Start = CMTime.Zero, Duration = asset.Duration };
|
||||
|
||||
// Remember the time range for the transition to the next item.
|
||||
var clipVideoTrack = asset.TracksWithMediaType(AVMediaType.Video)[0];
|
||||
compositionVideoTracks[alternatingIndex].InsertTimeRange(timeRangeInAsset, clipVideoTrack, nextClipStartTime, out NSError error);
|
||||
|
||||
if(i + 1 < clipsCount)
|
||||
{
|
||||
transitionTimeRanges [i] = new CMTimeRange {
|
||||
Start = nextClipStartTime,
|
||||
Duration = transitionDuration
|
||||
};
|
||||
var clipAudioTrack = asset.TracksWithMediaType(AVMediaType.Audio)[0];
|
||||
compositionAudioTracks[alternatingIndex].InsertTimeRange(timeRangeInAsset, clipAudioTrack, nextClipStartTime, out error);
|
||||
|
||||
}
|
||||
}
|
||||
// Remember the time range in which this clip should pass through.
|
||||
// First clip ends with a transition.
|
||||
// Second clip begins with a transition.
|
||||
// Exclude that transition from the pass through time ranges
|
||||
passThroughTimeRanges[i] = new CMTimeRange { Start = nextClipStartTime, Duration = timeRangeInAsset.Duration };
|
||||
|
||||
// Set up the video composition to perform cross dissolve or diagonal wipe transitions between clips.
|
||||
var instructions = new List<AVVideoCompositionInstruction> ();
|
||||
if (i > 0)
|
||||
{
|
||||
passThroughTimeRanges[i].Start = CMTime.Add(passThroughTimeRanges[i].Start, transitionDuration);
|
||||
passThroughTimeRanges[i].Duration = CMTime.Subtract(passThroughTimeRanges[i].Duration, transitionDuration);
|
||||
}
|
||||
|
||||
// Cycle between "pass through A", "transition from A to B", "pass through B"
|
||||
for(int i = 0; i < clipsCount; i++)
|
||||
{
|
||||
int alternatingIndex = i % 2;
|
||||
if (i + 1 < clipsCount)
|
||||
{
|
||||
passThroughTimeRanges[i].Duration = CMTime.Subtract(passThroughTimeRanges[i].Duration, transitionDuration);
|
||||
}
|
||||
|
||||
// if (videoComposition.CustomVideoCompositorClass != null) {
|
||||
// var videoInstruction = new CustomVideoCompositionInstruction (compositionVideoTracks [alternatingIndex].TrackID, passThroughTimeRanges [i]);
|
||||
// instructions.Add (videoInstruction);
|
||||
// } else {
|
||||
// // Pass through clip i.
|
||||
// var passThroughInstruction = AVMutableVideoCompositionInstruction.Create () as AVMutableVideoCompositionInstruction;
|
||||
// passThroughInstruction.TimeRange = passThroughTimeRanges [i];
|
||||
// var passThroughLayer = AVMutableVideoCompositionLayerInstruction.FromAssetTrack (compositionVideoTracks [alternatingIndex]);
|
||||
// passThroughInstruction.LayerInstructions = new [] { passThroughLayer };
|
||||
// instructions.Add (passThroughInstruction);
|
||||
//
|
||||
// }
|
||||
//TODO: Remove following call if previous works
|
||||
if (videoComposition.CustomVideoCompositorClass.Name != "nil") {
|
||||
var videoInstruction = new CustomVideoCompositionInstruction (compositionVideoTracks [alternatingIndex].TrackID, passThroughTimeRanges [i]);
|
||||
instructions.Add (videoInstruction);
|
||||
// The end of this clip will overlap the start of the next by transitionDuration.
|
||||
// (Note: this arithmetic falls apart if timeRangeInAsset.duration < 2 * transitionDuration.)
|
||||
nextClipStartTime = CMTime.Add(nextClipStartTime, timeRangeInAsset.Duration);
|
||||
nextClipStartTime = CMTime.Subtract(nextClipStartTime, transitionDuration);
|
||||
|
||||
}
|
||||
else {
|
||||
// Pass through clip i.
|
||||
var passThroughInstruction = AVMutableVideoCompositionInstruction.Create () as AVMutableVideoCompositionInstruction;
|
||||
passThroughInstruction.TimeRange = passThroughTimeRanges [i];
|
||||
var passThroughLayer = AVMutableVideoCompositionLayerInstruction.FromAssetTrack (compositionVideoTracks [alternatingIndex]);
|
||||
passThroughInstruction.LayerInstructions = new [] { passThroughLayer };
|
||||
instructions.Add (passThroughInstruction);
|
||||
// Remember the time range for the transition to the next item.
|
||||
if (i + 1 < clipsCount)
|
||||
{
|
||||
transitionTimeRanges[i] = new CMTimeRange { Start = nextClipStartTime, Duration = transitionDuration };
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// Set up the video composition to perform cross dissolve or diagonal wipe transitions between clips.
|
||||
var instructions = new List<AVVideoCompositionInstruction>();
|
||||
|
||||
if (i + 1 < clipsCount) {
|
||||
// Add transition from clip i to clip i+1.
|
||||
// if (videoComposition.CustomVideoCompositorClass != null) {
|
||||
// var videoInstruction = new CustomVideoCompositionInstruction (new NSNumber [] {
|
||||
// compositionVideoTracks [0].TrackID,
|
||||
// compositionVideoTracks [1].TrackID
|
||||
// }, transitionTimeRanges [1]);
|
||||
//
|
||||
// if (alternatingIndex == 0) {
|
||||
// videoInstruction.ForegroundTrackID = compositionVideoTracks [alternatingIndex].TrackID;
|
||||
// videoInstruction.BackgroundTrackID = compositionVideoTracks [1 - alternatingIndex].TrackID;
|
||||
// }
|
||||
//
|
||||
// instructions.Add (videoInstruction);
|
||||
// } else {
|
||||
// var transitionInstruction = AVMutableVideoCompositionInstruction.Create () as AVMutableVideoCompositionInstruction;
|
||||
// transitionInstruction.TimeRange = transitionTimeRanges [i];
|
||||
// var fromLayer = AVMutableVideoCompositionLayerInstruction.FromAssetTrack (compositionVideoTracks [alternatingIndex]);
|
||||
// var toLayer = AVMutableVideoCompositionLayerInstruction.FromAssetTrack (compositionVideoTracks [1 - alternatingIndex]);
|
||||
// transitionInstruction.LayerInstructions = new [] { toLayer, fromLayer };
|
||||
// instructions.Add (transitionInstruction);
|
||||
// }
|
||||
// TODO: remove following call if previous works
|
||||
if (videoComposition.CustomVideoCompositorClass.Name != "nil") {
|
||||
NSNumber[] sources = {
|
||||
new NSNumber (compositionVideoTracks [0].TrackID),
|
||||
new NSNumber (compositionVideoTracks [1].TrackID)
|
||||
};
|
||||
var videoInstructions = new CustomVideoCompositionInstruction (sources, transitionTimeRanges [i]);
|
||||
if (alternatingIndex == 0) {
|
||||
videoInstructions.ForegroundTrackID = compositionVideoTracks [alternatingIndex].TrackID;
|
||||
videoInstructions.BackgroundTrackID = compositionVideoTracks [1 - alternatingIndex].TrackID;
|
||||
}
|
||||
// Cycle between "pass through A", "transition from A to B", "pass through B"
|
||||
for (int i = 0; i < clipsCount; i++)
|
||||
{
|
||||
int alternatingIndex = i % 2; // alternating targets
|
||||
|
||||
instructions.Add (videoInstructions);
|
||||
Console.WriteLine ("Add transition from clip i to clip i+1");
|
||||
} else {
|
||||
var transitionInstruction = AVMutableVideoCompositionInstruction.Create () as AVMutableVideoCompositionInstruction;
|
||||
transitionInstruction.TimeRange = transitionTimeRanges [i];
|
||||
AVMutableVideoCompositionLayerInstruction fromLayer = AVMutableVideoCompositionLayerInstruction.FromAssetTrack (compositionVideoTracks [alternatingIndex]);
|
||||
AVMutableVideoCompositionLayerInstruction toLayer = AVMutableVideoCompositionLayerInstruction.FromAssetTrack (compositionVideoTracks [1 - alternatingIndex]);
|
||||
transitionInstruction.LayerInstructions = new AVVideoCompositionLayerInstruction[] {
|
||||
fromLayer,
|
||||
toLayer,
|
||||
};
|
||||
instructions.Add (transitionInstruction);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mutableVideoComposition.CustomVideoCompositorClass != null)
|
||||
{
|
||||
var videoInstruction = new CustomVideoCompositionInstruction(compositionVideoTracks[alternatingIndex].TrackID, passThroughTimeRanges[i]);
|
||||
instructions.Add(videoInstruction);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pass through clip i.
|
||||
var passThroughInstruction = AVMutableVideoCompositionInstruction.Create() as AVMutableVideoCompositionInstruction;
|
||||
passThroughInstruction.TimeRange = passThroughTimeRanges[i];
|
||||
|
||||
videoComposition.Instructions = instructions.ToArray ();
|
||||
}
|
||||
var passThroughLayer = AVMutableVideoCompositionLayerInstruction.FromAssetTrack(compositionVideoTracks[alternatingIndex]);
|
||||
passThroughInstruction.LayerInstructions = new[] { passThroughLayer };
|
||||
|
||||
public void BuildCompositionObjects(bool playBack)
|
||||
{
|
||||
if (Clips == null || Clips.Count == 0) {
|
||||
Composition = null;
|
||||
VideoComposition = null;
|
||||
return;
|
||||
}
|
||||
instructions.Add(passThroughInstruction);
|
||||
}
|
||||
|
||||
var videoSize = Clips [0].NaturalSize;
|
||||
var composition = AVMutableComposition.Create ();
|
||||
AVMutableVideoComposition videoComposition;
|
||||
if (i + 1 < clipsCount)
|
||||
{
|
||||
// Add transition from clip i to clip i+1.
|
||||
if (mutableVideoComposition.CustomVideoCompositorClass != null)
|
||||
{
|
||||
var videoInstruction = new CustomVideoCompositionInstruction(new NSNumber[]
|
||||
{
|
||||
compositionVideoTracks[0].TrackID,
|
||||
compositionVideoTracks[1].TrackID
|
||||
}, transitionTimeRanges[i]);
|
||||
|
||||
composition.NaturalSize = videoSize;
|
||||
if (alternatingIndex == 0)
|
||||
{
|
||||
// First track -> Foreground track while compositing
|
||||
videoInstruction.ForegroundTrackId = compositionVideoTracks[alternatingIndex].TrackID;
|
||||
// Second track -> Background track while compositing
|
||||
videoInstruction.BackgroundTrackId = compositionVideoTracks[1 - alternatingIndex].TrackID;
|
||||
}
|
||||
|
||||
// With transitions:
|
||||
// Place clips into alternating video & audio tracks in composition, overlapped by transitionDuration.
|
||||
// Set up the video composition to cycle between "pass through A", "transition from A to B",
|
||||
// "pass through B".
|
||||
instructions.Add(videoInstruction);
|
||||
}
|
||||
else
|
||||
{
|
||||
var transitionInstruction = AVMutableVideoCompositionInstruction.Create() as AVMutableVideoCompositionInstruction;
|
||||
transitionInstruction.TimeRange = transitionTimeRanges[i];
|
||||
|
||||
videoComposition = AVMutableVideoComposition.Create ();
|
||||
var fromLayer = AVMutableVideoCompositionLayerInstruction.FromAssetTrack(compositionVideoTracks[alternatingIndex]);
|
||||
var toLayer = AVMutableVideoCompositionLayerInstruction.FromAssetTrack(compositionVideoTracks[1 - alternatingIndex]);
|
||||
|
||||
//Set CustomVideoCompositorClass based on the Compositor user selected.
|
||||
transitionInstruction.LayerInstructions = new[] { toLayer, fromLayer };
|
||||
instructions.Add(transitionInstruction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TransitionType == TransitionTypeController.DiagonalWipeTransition) {
|
||||
videoComposition.CustomVideoCompositorClass = new ObjCRuntime.Class (typeof(DiagonalWipeCompositor));
|
||||
} else {
|
||||
videoComposition.CustomVideoCompositorClass = new ObjCRuntime.Class (typeof(CrossDissolveCompositor));
|
||||
}
|
||||
mutableVideoComposition.Instructions = instructions.ToArray();
|
||||
}
|
||||
|
||||
buildTransitionComposition (composition, videoComposition);
|
||||
if (videoComposition != null) {
|
||||
// Every videoComposition needs these properties to be set:
|
||||
videoComposition.FrameDuration = new CMTime (1, 30);
|
||||
videoComposition.RenderSize = videoSize;
|
||||
public void BuildCompositionObjectsForPlayback(bool playback)
|
||||
{
|
||||
if (Clips == null || Clips.Count == 0)
|
||||
{
|
||||
this.composition = null;
|
||||
this.videoComposition = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var videoSize = Clips[0].NaturalSize;
|
||||
var mutableComposition = AVMutableComposition.Create();
|
||||
AVMutableVideoComposition mutableVideoComposition = null;
|
||||
|
||||
}
|
||||
Composition = composition;
|
||||
VideoComposition = videoComposition;
|
||||
}
|
||||
mutableComposition.NaturalSize = videoSize;
|
||||
|
||||
public AVAssetExportSession AssetExportSession (string presetName)
|
||||
{
|
||||
var session = new AVAssetExportSession (Composition, presetName);
|
||||
session.VideoComposition = VideoComposition;
|
||||
return session;
|
||||
}
|
||||
// With transitions:
|
||||
// Place clips into alternating video & audio tracks in composition, overlapped by transitionDuration.
|
||||
// Set up the video composition to cycle between "pass through A", "transition from A to B",
|
||||
// "pass through B".
|
||||
|
||||
public AVPlayerItem PlayerItem {
|
||||
get {
|
||||
AVPlayerItem playerItem = AVPlayerItem.FromAsset (Composition);
|
||||
//TODO:Got an NullReferenceException here if this.videoComposition.CustomVideoCompositorClass is not a "Nil" Class
|
||||
playerItem.VideoComposition = VideoComposition;
|
||||
//If set the CustomVideoCompositorClass in playerItem.VideoComposition, will get a System.NotImplementedException
|
||||
//playerItem.VideoComposition.CustomVideoCompositorClass = new MonoTouch.ObjCRuntime.Class(typeof(CrossDissolveCompositor));
|
||||
return playerItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mutableVideoComposition = AVMutableVideoComposition.Create();
|
||||
if (TransitionType == TransitionType.DiagonalWipeTransition)
|
||||
{
|
||||
mutableVideoComposition.CustomVideoCompositorClass = new ObjCRuntime.Class(typeof(DiagonalWipeCompositor));
|
||||
}
|
||||
else
|
||||
{
|
||||
mutableVideoComposition.CustomVideoCompositorClass = new ObjCRuntime.Class(typeof(CrossDissolveCompositor));
|
||||
}
|
||||
|
||||
BuildTransitionComposition(mutableComposition, mutableVideoComposition);
|
||||
if (mutableVideoComposition != null)
|
||||
{
|
||||
// Every videoComposition needs these properties to be set:
|
||||
mutableVideoComposition.FrameDuration = new CMTime(1, 30);
|
||||
mutableVideoComposition.RenderSize = videoSize;
|
||||
}
|
||||
|
||||
this.composition = mutableComposition;
|
||||
this.videoComposition = mutableVideoComposition;
|
||||
}
|
||||
}
|
||||
|
||||
public AVAssetExportSession AssetExportSessionWithPreset(string presetName)
|
||||
{
|
||||
return new AVAssetExportSession(composition, presetName)
|
||||
{
|
||||
VideoComposition = videoComposition
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,84 +1,68 @@
|
|||
// This file has been autogenerated from a class added in the UI designer.
|
||||
|
||||
using System;
|
||||
|
||||
using Foundation;
|
||||
using System;
|
||||
using UIKit;
|
||||
|
||||
namespace AVCustomEdit
|
||||
{
|
||||
public partial class TransitionTypeController : UITableViewController
|
||||
{
|
||||
public Action<int> TransitionTypePicked;
|
||||
public interface ITransitionTypePickerDelegate
|
||||
{
|
||||
void DidPickTransitionType(TransitionType transitionType);
|
||||
}
|
||||
|
||||
public const int DiagonalWipeTransition = 0;
|
||||
public const int CrossDissolveTransition = 1;
|
||||
public partial class TransitionTypeController : UITableViewController
|
||||
{
|
||||
public TransitionTypeController(IntPtr handle) : base(handle) { }
|
||||
|
||||
public UITableViewCell CrossDissolveCell {
|
||||
get {
|
||||
return crossDissolveCell;
|
||||
}
|
||||
set {
|
||||
crossDissolveCell = value;
|
||||
}
|
||||
}
|
||||
public ITransitionTypePickerDelegate Delegate { get; set; }
|
||||
|
||||
public UITableViewCell DiagonalWipeCell {
|
||||
get {
|
||||
return diagonalWipeCell;
|
||||
}
|
||||
set {
|
||||
diagonalWipeCell = value;
|
||||
}
|
||||
}
|
||||
public TransitionType CurrentTransition { get; set; }
|
||||
|
||||
public int CurrentTransition;
|
||||
public override void ViewDidLoad()
|
||||
{
|
||||
base.ViewDidLoad();
|
||||
|
||||
public TransitionTypeController (IntPtr handle) : base (handle)
|
||||
{
|
||||
}
|
||||
switch (this.CurrentTransition)
|
||||
{
|
||||
case TransitionType.CrossDissolveTransition:
|
||||
this.crossDissolveCell.Accessory = UITableViewCellAccessory.Checkmark;
|
||||
break;
|
||||
|
||||
public override void ViewDidLoad ()
|
||||
{
|
||||
base.ViewDidLoad ();
|
||||
case TransitionType.DiagonalWipeTransition:
|
||||
this.diagonalWipeCell.Accessory = UITableViewCellAccessory.Checkmark;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tableView.Delegate = new TableViewDelegate ();
|
||||
}
|
||||
partial void TransitionSelected(UIBarButtonItem sender)
|
||||
{
|
||||
this.Delegate.DidPickTransitionType(this.CurrentTransition);
|
||||
}
|
||||
|
||||
partial void TransitionSelected (NSObject sender)
|
||||
{
|
||||
TransitionTypePicked (CurrentTransition);
|
||||
}
|
||||
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
|
||||
{
|
||||
var selectedCell = tableView.CellAt(indexPath);
|
||||
selectedCell.Accessory = UITableViewCellAccessory.Checkmark;
|
||||
|
||||
public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
|
||||
{
|
||||
UITableViewCell selectedCell = tableView.CellAt (indexPath);
|
||||
selectedCell.EditingAccessory = UITableViewCellAccessory.Checkmark;
|
||||
if (selectedCell == this.diagonalWipeCell)
|
||||
{
|
||||
this.crossDissolveCell.Accessory = UITableViewCellAccessory.None;
|
||||
this.CurrentTransition = TransitionType.DiagonalWipeTransition;
|
||||
this.Delegate.DidPickTransitionType(TransitionType.DiagonalWipeTransition);
|
||||
}
|
||||
else if (selectedCell == this.crossDissolveCell)
|
||||
{
|
||||
this.diagonalWipeCell.Accessory = UITableViewCellAccessory.None;
|
||||
this.CurrentTransition = TransitionType.CrossDissolveTransition;
|
||||
this.Delegate.DidPickTransitionType(TransitionType.CrossDissolveTransition);
|
||||
}
|
||||
|
||||
switch (selectedCell.Tag) {
|
||||
case TransitionTypeController.DiagonalWipeTransition:
|
||||
CrossDissolveCell.Accessory = UITableViewCellAccessory.None;
|
||||
if (TransitionTypePicked != null)
|
||||
TransitionTypePicked (TransitionTypeController.DiagonalWipeTransition);
|
||||
CurrentTransition = DiagonalWipeTransition;
|
||||
break;
|
||||
case TransitionTypeController.CrossDissolveTransition:
|
||||
DiagonalWipeCell.Accessory = UITableViewCellAccessory.None;
|
||||
if (TransitionTypePicked != null)
|
||||
TransitionTypePicked (TransitionTypeController.CrossDissolveTransition);
|
||||
CurrentTransition = CrossDissolveTransition;
|
||||
break;
|
||||
}
|
||||
tableView.DeselectRow(indexPath, true);
|
||||
}
|
||||
}
|
||||
|
||||
tableView.DeselectRow (indexPath, true);
|
||||
}
|
||||
}
|
||||
|
||||
public class TableViewDelegate : UITableViewDelegate
|
||||
{
|
||||
public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
public enum TransitionType
|
||||
{
|
||||
DiagonalWipeTransition = 0,
|
||||
CrossDissolveTransition = 1,
|
||||
}
|
||||
}
|
|
@ -1,45 +1,42 @@
|
|||
// WARNING
|
||||
//
|
||||
// This file has been generated automatically by Xamarin Studio to store outlets and
|
||||
// actions made in the UI designer. If it is removed, they will be lost.
|
||||
// Manual changes to this file may not be handled correctly.
|
||||
//
|
||||
using Foundation;
|
||||
using System.CodeDom.Compiler;
|
||||
|
||||
namespace AVCustomEdit
|
||||
{
|
||||
[Register ("TransitionTypeController")]
|
||||
partial class TransitionTypeController
|
||||
{
|
||||
[Outlet]
|
||||
UIKit.UITableViewCell crossDissolveCell { get; set; }
|
||||
|
||||
[Outlet]
|
||||
UIKit.UITableViewCell diagonalWipeCell { get; set; }
|
||||
|
||||
[Outlet]
|
||||
UIKit.UITableView tableView { get; set; }
|
||||
|
||||
[Action ("TransitionSelected:")]
|
||||
partial void TransitionSelected (Foundation.NSObject sender);
|
||||
|
||||
void ReleaseDesignerOutlets ()
|
||||
{
|
||||
if (crossDissolveCell != null) {
|
||||
crossDissolveCell.Dispose ();
|
||||
crossDissolveCell = null;
|
||||
}
|
||||
|
||||
if (diagonalWipeCell != null) {
|
||||
diagonalWipeCell.Dispose ();
|
||||
diagonalWipeCell = null;
|
||||
}
|
||||
|
||||
if (tableView != null) {
|
||||
tableView.Dispose ();
|
||||
tableView = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// WARNING
|
||||
//
|
||||
// This file has been generated automatically by Visual Studio from the outlets and
|
||||
// actions declared in your storyboard file.
|
||||
// Manual changes to this file will not be maintained.
|
||||
//
|
||||
using Foundation;
|
||||
using System;
|
||||
using System.CodeDom.Compiler;
|
||||
using UIKit;
|
||||
|
||||
namespace AVCustomEdit
|
||||
{
|
||||
[Register ("TransitionTypeController")]
|
||||
partial class TransitionTypeController
|
||||
{
|
||||
[Outlet]
|
||||
[GeneratedCode ("iOS Designer", "1.0")]
|
||||
UIKit.UITableViewCell crossDissolveCell { get; set; }
|
||||
|
||||
[Outlet]
|
||||
[GeneratedCode ("iOS Designer", "1.0")]
|
||||
UIKit.UITableViewCell diagonalWipeCell { get; set; }
|
||||
|
||||
[Action ("TransitionSelected:")]
|
||||
[GeneratedCode ("iOS Designer", "1.0")]
|
||||
partial void TransitionSelected (UIKit.UIBarButtonItem sender);
|
||||
|
||||
void ReleaseDesignerOutlets ()
|
||||
{
|
||||
if (crossDissolveCell != null) {
|
||||
crossDissolveCell.Dispose ();
|
||||
crossDissolveCell = null;
|
||||
}
|
||||
|
||||
if (diagonalWipeCell != null) {
|
||||
diagonalWipeCell.Dispose ();
|
||||
diagonalWipeCell = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,591 @@
|
|||
using AssetsLibrary;
|
||||
using AVFoundation;
|
||||
using CoreFoundation;
|
||||
using CoreMedia;
|
||||
using Foundation;
|
||||
using Photos;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UIKit;
|
||||
|
||||
namespace AVCustomEdit
|
||||
{
|
||||
public partial class ViewController : UIViewController, IUIGestureRecognizerDelegate, ITransitionTypePickerDelegate
|
||||
{
|
||||
private readonly NSString StatusObservationContext = new NSString("AVCustomEditPlayerViewControllerStatusObservationContext");
|
||||
private readonly NSString RateObservationContext = new NSString("AVCustomEditPlayerViewControllerRateObservationContext");
|
||||
|
||||
private SimpleEditor editor;
|
||||
|
||||
private List<AVAsset> clips;
|
||||
private List<NSValue> clipTimeRanges;
|
||||
|
||||
private AVPlayer player;
|
||||
private AVPlayerItem playerItem;
|
||||
|
||||
private bool isPlaying;
|
||||
private bool isScrubInFlight;
|
||||
private bool isSeekToZeroBeforePlaying;
|
||||
private float lastScrubSliderValue;
|
||||
private float playRateToRestore;
|
||||
private NSObject timeObserver;
|
||||
|
||||
private float transitionDuration;
|
||||
private TransitionType transitionType;
|
||||
private bool isTransitionsEnabled;
|
||||
|
||||
private NSTimer progressTimer;
|
||||
|
||||
protected ViewController(IntPtr handle) : base(handle) { }
|
||||
|
||||
public override void ViewDidLoad()
|
||||
{
|
||||
base.ViewDidLoad();
|
||||
|
||||
this.editor = new SimpleEditor();
|
||||
this.clips = new List<AVAsset>();
|
||||
this.clipTimeRanges = new List<NSValue>();
|
||||
|
||||
// Defaults for the transition settings.
|
||||
this.transitionType = TransitionType.DiagonalWipeTransition;
|
||||
this.transitionDuration = 2f;
|
||||
this.isTransitionsEnabled = true;
|
||||
|
||||
this.UpdateScrubber();
|
||||
this.UpdateTimeLabel();
|
||||
|
||||
// Add the clips from the main bundle to create a composition using them
|
||||
this.SetupEditingAndPlayback();
|
||||
}
|
||||
|
||||
public override void ViewDidAppear(bool animated)
|
||||
{
|
||||
base.ViewDidAppear(animated);
|
||||
|
||||
if (this.player == null)
|
||||
{
|
||||
this.isSeekToZeroBeforePlaying = false;
|
||||
|
||||
this.player = new AVPlayer();
|
||||
this.player.AddObserver(this, "rate", NSKeyValueObservingOptions.Old | NSKeyValueObservingOptions.New, RateObservationContext.Handle);
|
||||
this.playerView.Player = this.player;
|
||||
}
|
||||
|
||||
this.AddTimeObserverToPlayer();
|
||||
|
||||
// Build AVComposition and AVVideoComposition objects for playback
|
||||
this.editor.BuildCompositionObjectsForPlayback(true);
|
||||
this.SynchronizePlayerWithEditor();
|
||||
}
|
||||
|
||||
public override void ViewWillDisappear(bool animated)
|
||||
{
|
||||
base.ViewWillDisappear(animated);
|
||||
this.player.Pause();
|
||||
this.RemoveTimeObserverFromPlayer();
|
||||
}
|
||||
|
||||
public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
|
||||
{
|
||||
if (segue.Identifier == "Transition")
|
||||
{
|
||||
// Setup transition type picker controller before it is shown.
|
||||
if ((segue.DestinationViewController as UINavigationController)?.TopViewController is TransitionTypeController transitionTypePickerController)
|
||||
{
|
||||
transitionTypePickerController.Delegate = this;
|
||||
transitionTypePickerController.CurrentTransition = this.transitionType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Simple Editor
|
||||
|
||||
private void SetupEditingAndPlayback()
|
||||
{
|
||||
var path1 = NSBundle.MainBundle.PathForResource("sample_clip1", "m4v");
|
||||
var path2 = NSBundle.MainBundle.PathForResource("sample_clip2", "mov");
|
||||
var asset1 = AVAsset.FromUrl(new NSUrl(path1, false)) as AVUrlAsset;
|
||||
var asset2 = AVAsset.FromUrl(new NSUrl(path2, false)) as AVUrlAsset;
|
||||
|
||||
var dispatchGroup = DispatchGroup.Create();
|
||||
string[] assetKeysToLoadAndTest = { "tracks", "duration", "composable" };
|
||||
|
||||
this.LoadAsset(asset1, assetKeysToLoadAndTest, dispatchGroup);
|
||||
this.LoadAsset(asset2, assetKeysToLoadAndTest, dispatchGroup);
|
||||
|
||||
// Wait until both assets are loaded
|
||||
dispatchGroup.Wait(DispatchTime.Forever);
|
||||
base.InvokeOnMainThread(() => this.SynchronizeWithEditor());
|
||||
}
|
||||
|
||||
private void LoadAsset(AVAsset asset, string[] assetKeysToLoad, DispatchGroup dispatchGroup)
|
||||
{
|
||||
dispatchGroup.Enter();
|
||||
asset.LoadValuesAsynchronously(assetKeysToLoad, () =>
|
||||
{
|
||||
// First test whether the values of each of the keys we need have been successfully loaded.
|
||||
foreach (var key in assetKeysToLoad)
|
||||
{
|
||||
if (asset.StatusOfValue(key, out NSError error) == AVKeyValueStatus.Failed)
|
||||
{
|
||||
Console.WriteLine($"Key value loading failed for key:{key} with error: {error?.LocalizedDescription ?? ""}");
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
|
||||
if (!asset.Composable)
|
||||
{
|
||||
Console.WriteLine("Asset is not composable");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
this.clips.Add(asset);
|
||||
// This code assumes that both assets are atleast 5 seconds long.
|
||||
var value = NSValue.FromCMTimeRange(new CMTimeRange { Start = CMTime.FromSeconds(0, 1), Duration = CMTime.FromSeconds(5, 1) });
|
||||
this.clipTimeRanges.Add(value);
|
||||
|
||||
bail:
|
||||
dispatchGroup.Leave();
|
||||
});
|
||||
}
|
||||
|
||||
private void SynchronizePlayerWithEditor()
|
||||
{
|
||||
if (this.player != null)
|
||||
{
|
||||
var playerItem = this.editor.PlayerItem;
|
||||
if (this.playerItem != playerItem)
|
||||
{
|
||||
if (this.playerItem != null)
|
||||
{
|
||||
this.playerItem.RemoveObserver(this, "status");
|
||||
NSNotificationCenter.DefaultCenter.RemoveObserver(this, AVPlayerItem.DidPlayToEndTimeNotification, this.playerItem);
|
||||
}
|
||||
|
||||
this.playerItem = playerItem;
|
||||
if (this.playerItem != null)
|
||||
{
|
||||
this.playerItem.SeekingWaitsForVideoCompositionRendering = true;
|
||||
|
||||
// Observe the player item "status" key to determine when it is ready to play
|
||||
this.playerItem.AddObserver(this, "status", NSKeyValueObservingOptions.New | NSKeyValueObservingOptions.Initial, StatusObservationContext.Handle);
|
||||
|
||||
// When the player item has played to its end time we'll set a flag
|
||||
// so that the next time the play method is issued the player will
|
||||
// be reset to time zero first.
|
||||
NSNotificationCenter.DefaultCenter.AddObserver(AVPlayerItem.DidPlayToEndTimeNotification, this.PlayerItemDidReachEnd);
|
||||
}
|
||||
|
||||
this.player.ReplaceCurrentItemWithPlayerItem(this.playerItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SynchronizeWithEditor()
|
||||
{
|
||||
// Clips
|
||||
this.SynchronizeEditorClipsWithOurClips();
|
||||
this.SynchronizeEditorClipTimeRangesWithOurClipTimeRanges();
|
||||
|
||||
// Transitions
|
||||
if (this.isTransitionsEnabled)
|
||||
{
|
||||
this.editor.TransitionDuration = CMTime.FromSeconds(this.transitionDuration, 600);
|
||||
this.editor.TransitionType = this.transitionType;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.editor.TransitionDuration = CMTime.Invalid;
|
||||
}
|
||||
|
||||
// Build AVComposition and AVVideoComposition objects for playback
|
||||
//this.editor.BuildCompositionObjectsForPlayback(true);
|
||||
//this.SynchronizePlayerWithEditor();
|
||||
}
|
||||
|
||||
private void SynchronizeEditorClipsWithOurClips()
|
||||
{
|
||||
var validClips = new List<AVAsset>();
|
||||
foreach (var asset in this.clips)
|
||||
{
|
||||
if (asset != null)
|
||||
{
|
||||
validClips.Add(asset);
|
||||
}
|
||||
}
|
||||
|
||||
this.editor.Clips = validClips;
|
||||
}
|
||||
|
||||
private void SynchronizeEditorClipTimeRangesWithOurClipTimeRanges()
|
||||
{
|
||||
var validClipTimeRanges = new List<NSValue>();
|
||||
foreach (var timeRange in this.clipTimeRanges)
|
||||
{
|
||||
if (timeRange != null)
|
||||
{
|
||||
validClipTimeRanges.Add(timeRange);
|
||||
}
|
||||
}
|
||||
|
||||
this.editor.ClipTimeRanges = validClipTimeRanges;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private const int NSEC_PER_SEC = 1000000000;
|
||||
|
||||
/// <summary>
|
||||
/// Update the scrubber and time label periodically.
|
||||
/// </summary>
|
||||
private void AddTimeObserverToPlayer()
|
||||
{
|
||||
if (this.player?.CurrentItem != null &&
|
||||
this.player.CurrentItem.Status == AVPlayerItemStatus.ReadyToPlay)
|
||||
{
|
||||
var duration = this.GetPlayerItemDuration().Seconds;
|
||||
if (!double.IsInfinity(duration))
|
||||
{
|
||||
var width = this.scrubber.Bounds.Width;
|
||||
var interval = 0.5 * duration / width;
|
||||
|
||||
/* The time label needs to update at least once per second. */
|
||||
if (interval > 1.0)
|
||||
{
|
||||
interval = 1.0;
|
||||
}
|
||||
|
||||
timeObserver = this.player.AddPeriodicTimeObserver(CMTime.FromSeconds(interval, NSEC_PER_SEC),
|
||||
DispatchQueue.MainQueue,
|
||||
(time) =>
|
||||
{
|
||||
this.UpdateScrubber();
|
||||
this.UpdateTimeLabel();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveTimeObserverFromPlayer()
|
||||
{
|
||||
if (this.timeObserver != null)
|
||||
{
|
||||
this.player.RemoveTimeObserver(this.timeObserver);
|
||||
this.timeObserver.Dispose();
|
||||
this.timeObserver = null;
|
||||
}
|
||||
}
|
||||
|
||||
private CMTime GetPlayerItemDuration()
|
||||
{
|
||||
var itemDuration = CMTime.Invalid;
|
||||
|
||||
var playerItem = this.player?.CurrentItem;
|
||||
if (playerItem?.Status == AVPlayerItemStatus.ReadyToPlay)
|
||||
{
|
||||
itemDuration = playerItem.Duration;
|
||||
}
|
||||
|
||||
// Will be kCMTimeInvalid if the item is not ready to play.
|
||||
return itemDuration;
|
||||
}
|
||||
|
||||
public override void ObserveValue(NSString keyPath, NSObject ofObject, NSDictionary change, IntPtr context)
|
||||
{
|
||||
if (context == this.RateObservationContext.Handle)
|
||||
{
|
||||
var changes = new NSObservedChange(change);
|
||||
if (changes.NewValue is NSNumber newValue &&
|
||||
(changes.OldValue == null || (changes.OldValue is NSNumber oldRate && oldRate.FloatValue != newValue.FloatValue)))
|
||||
{
|
||||
this.isPlaying = (newValue.FloatValue != 0f) || (playRateToRestore != 0f);
|
||||
this.UpdatePlayPauseButton();
|
||||
this.UpdateScrubber();
|
||||
this.UpdateTimeLabel();
|
||||
|
||||
// clear
|
||||
newValue.Dispose();
|
||||
newValue = null;
|
||||
}
|
||||
}
|
||||
else if (context == this.StatusObservationContext.Handle)
|
||||
{
|
||||
if (ofObject is AVPlayerItem playerItem)
|
||||
{
|
||||
if (playerItem.Status == AVPlayerItemStatus.ReadyToPlay)
|
||||
{
|
||||
/* Once the AVPlayerItem becomes ready to play, i.e.
|
||||
[playerItem status] == AVPlayerItemStatusReadyToPlay,
|
||||
its duration can be fetched from the item. */
|
||||
|
||||
this.AddTimeObserverToPlayer();
|
||||
}
|
||||
else if (playerItem.Status == AVPlayerItemStatus.Failed)
|
||||
{
|
||||
this.ReportError(this.playerItem.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
base.ObserveValue(keyPath, ofObject, change, context);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePlayPauseButton()
|
||||
{
|
||||
var style = this.isPlaying ? UIBarButtonSystemItem.Pause : UIBarButtonSystemItem.Play;
|
||||
using (var newPlayPauseButton = new UIBarButtonItem(style, (sender, e) => this.TogglePlayPause(sender as UIBarButtonItem)))
|
||||
{
|
||||
var items = this.toolbar.Items;
|
||||
items[0] = newPlayPauseButton;
|
||||
this.toolbar.SetItems(items, false);
|
||||
|
||||
this.playPauseButton = newPlayPauseButton;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTimeLabel()
|
||||
{
|
||||
var seconds = this.player != null ? this.player.CurrentTime.Seconds : 0d;
|
||||
if (double.IsNaN(seconds))
|
||||
{
|
||||
seconds = 0;
|
||||
}
|
||||
|
||||
this.currentTimeLabel.TextColor = UIColor.White;
|
||||
this.currentTimeLabel.TextAlignment = UITextAlignment.Center;
|
||||
this.currentTimeLabel.Text = TimeSpan.FromSeconds(seconds).ToString(@"mm\:ss");
|
||||
}
|
||||
|
||||
private void UpdateScrubber()
|
||||
{
|
||||
var duration = this.GetPlayerItemDuration().Seconds;
|
||||
if (!double.IsNaN(duration))
|
||||
{
|
||||
var time = this.player.CurrentTime.Seconds;
|
||||
this.scrubber.Value = (float)(time / duration);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.scrubber.Value = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateProgress(AVAssetExportSession session)
|
||||
{
|
||||
if (session?.Status == AVAssetExportSessionStatus.Exporting)
|
||||
{
|
||||
this.exportProgressView.Progress = session.Progress;
|
||||
}
|
||||
}
|
||||
|
||||
private void ReportError(NSError error)
|
||||
{
|
||||
if (error != null)
|
||||
{
|
||||
DispatchQueue.MainQueue.DispatchAsync(() =>
|
||||
{
|
||||
var alertView = UIAlertController.Create(error.LocalizedDescription, error.LocalizedRecoverySuggestion, UIAlertControllerStyle.Alert);
|
||||
alertView.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Default, null));
|
||||
base.PresentViewController(alertView, true, null);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IBActions
|
||||
|
||||
partial void TogglePlayPause(UIBarButtonItem sender)
|
||||
{
|
||||
this.isPlaying = !this.isPlaying;
|
||||
if (this.isPlaying)
|
||||
{
|
||||
if (this.isSeekToZeroBeforePlaying)
|
||||
{
|
||||
this.player.Seek(CMTime.Zero);
|
||||
this.isSeekToZeroBeforePlaying = false;
|
||||
}
|
||||
|
||||
this.player.Play();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.player.Pause();
|
||||
}
|
||||
}
|
||||
|
||||
partial void BeginScrubbing(UISlider sender)
|
||||
{
|
||||
this.isSeekToZeroBeforePlaying = false;
|
||||
this.playRateToRestore = this.player.Rate;
|
||||
this.player.Rate = 0f;
|
||||
|
||||
this.RemoveTimeObserverFromPlayer();
|
||||
}
|
||||
|
||||
partial void Scrub(UISlider sender)
|
||||
{
|
||||
this.lastScrubSliderValue = this.scrubber.Value;
|
||||
if (!this.isScrubInFlight)
|
||||
{
|
||||
this.ScrubToSliderValue(this.lastScrubSliderValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void ScrubToSliderValue(float sliderValue)
|
||||
{
|
||||
var duration = this.GetPlayerItemDuration().Seconds;
|
||||
if (!double.IsInfinity(duration))
|
||||
{
|
||||
var width = this.scrubber.Bounds.Width;
|
||||
|
||||
double time = duration * sliderValue;
|
||||
double tolerance = 1d * duration / width;
|
||||
|
||||
this.isScrubInFlight = true;
|
||||
|
||||
this.player.Seek(CMTime.FromSeconds(time, NSEC_PER_SEC),
|
||||
CMTime.FromSeconds(tolerance, NSEC_PER_SEC),
|
||||
CMTime.FromSeconds(tolerance, NSEC_PER_SEC),
|
||||
(finished) =>
|
||||
{
|
||||
this.isScrubInFlight = false;
|
||||
this.UpdateTimeLabel();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
partial void EndScrubbing(UISlider sender)
|
||||
{
|
||||
if (this.isScrubInFlight)
|
||||
{
|
||||
this.ScrubToSliderValue(this.lastScrubSliderValue);
|
||||
}
|
||||
|
||||
this.AddTimeObserverToPlayer();
|
||||
|
||||
this.player.Rate = this.playRateToRestore;
|
||||
this.playRateToRestore = 0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the player item has played to its end time
|
||||
/// </summary>
|
||||
private void PlayerItemDidReachEnd(NSNotification obj)
|
||||
{
|
||||
// After the movie has played to its end time, seek back to time zero to play it again.
|
||||
this.isSeekToZeroBeforePlaying = true;
|
||||
}
|
||||
|
||||
partial void HandleTapGesture(UITapGestureRecognizer sender)
|
||||
{
|
||||
this.toolbar.Hidden = !this.toolbar.Hidden;
|
||||
this.currentTimeLabel.Hidden = !this.currentTimeLabel.Hidden;
|
||||
}
|
||||
|
||||
partial void ExportToMovie(UIBarButtonItem sender)
|
||||
{
|
||||
this.exportProgressView.Hidden = false;
|
||||
|
||||
this.player.Pause();
|
||||
this.playPauseButton.Enabled = false;
|
||||
this.transitionButton.Enabled = false;
|
||||
this.scrubber.Enabled = false;
|
||||
this.exportButton.Enabled = false;
|
||||
|
||||
this.editor.BuildCompositionObjectsForPlayback(false);
|
||||
|
||||
// Get the export session from the editor
|
||||
var session = this.editor.AssetExportSessionWithPreset(AVAssetExportSession.PresetMediumQuality);
|
||||
|
||||
var filePath = Path.Combine(Path.GetTempPath(), "ExportedProject.mov");
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
File.Delete(filePath);
|
||||
}
|
||||
|
||||
// If a preset that is not compatible with AVFileTypeQuickTimeMovie is used, one can use -[AVAssetExportSession supportedFileTypes] to obtain a supported file type for the output file and UTTypeCreatePreferredIdentifierForTag to obtain an appropriate path extension for the output file type.
|
||||
session.OutputUrl = NSUrl.FromFilename(filePath);
|
||||
session.OutputFileType = AVFileType.QuickTimeMovie;
|
||||
|
||||
session.ExportAsynchronously(() => DispatchQueue.MainQueue.DispatchAsync(() => OnExportCompleted(session)));
|
||||
|
||||
// Update progress view with export progress
|
||||
this.progressTimer = NSTimer.CreateRepeatingTimer(0.5, d => this.UpdateProgress(session));
|
||||
NSRunLoop.Current.AddTimer(this.progressTimer, NSRunLoopMode.Default);
|
||||
}
|
||||
|
||||
private void OnExportCompleted(AVAssetExportSession session)
|
||||
{
|
||||
this.exportProgressView.Hidden = true;
|
||||
this.currentTimeLabel.Hidden = false;
|
||||
var outputURL = session.OutputUrl;
|
||||
|
||||
this.progressTimer.Invalidate();
|
||||
this.progressTimer.Dispose();
|
||||
this.progressTimer = null;
|
||||
|
||||
if (session.Status != AVAssetExportSessionStatus.Completed)
|
||||
{
|
||||
Console.WriteLine($"exportSession error:{session.Error}");
|
||||
this.ReportError(session.Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.exportProgressView.Progress = 1f;
|
||||
|
||||
// Save the exported movie to the camera roll
|
||||
PHPhotoLibrary.RequestAuthorization((status) =>
|
||||
{
|
||||
if(status == PHAuthorizationStatus.Authorized)
|
||||
{
|
||||
PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => PHAssetChangeRequest.FromVideo(outputURL),
|
||||
(successfully, error) =>
|
||||
{
|
||||
if (error != null)
|
||||
{
|
||||
Console.WriteLine($"writeVideoToAssestsLibrary failed: {error}");
|
||||
this.ReportError(error);
|
||||
}
|
||||
|
||||
base.InvokeOnMainThread(() =>
|
||||
{
|
||||
this.playPauseButton.Enabled = true;
|
||||
this.transitionButton.Enabled = true;
|
||||
this.scrubber.Enabled = true;
|
||||
this.exportButton.Enabled = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Gesture recognizer delegate
|
||||
|
||||
[Export("gestureRecognizer:shouldReceiveTouch:")]
|
||||
public bool ShouldReceiveTouch(UIGestureRecognizer recognizer, UITouch touch)
|
||||
{
|
||||
return touch.View == this.playerView;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void DidPickTransitionType(TransitionType transitionType)
|
||||
{
|
||||
this.transitionType = transitionType;
|
||||
|
||||
// Let the editor know of the change in transition type.
|
||||
this.SynchronizeWithEditor();
|
||||
|
||||
this.DismissViewController(true, null);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
// WARNING
|
||||
//
|
||||
// This file has been generated automatically by Visual Studio from the outlets and
|
||||
// actions declared in your storyboard file.
|
||||
// Manual changes to this file will not be maintained.
|
||||
//
|
||||
using Foundation;
|
||||
using System;
|
||||
using System.CodeDom.Compiler;
|
||||
|
||||
namespace AVCustomEdit
|
||||
{
|
||||
[Register ("ViewController")]
|
||||
partial class ViewController
|
||||
{
|
||||
[Outlet]
|
||||
[GeneratedCode ("iOS Designer", "1.0")]
|
||||
UIKit.UILabel currentTimeLabel { get; set; }
|
||||
|
||||
[Outlet]
|
||||
[GeneratedCode ("iOS Designer", "1.0")]
|
||||
UIKit.UIBarButtonItem exportButton { get; set; }
|
||||
|
||||
[Outlet]
|
||||
[GeneratedCode ("iOS Designer", "1.0")]
|
||||
UIKit.UIProgressView exportProgressView { get; set; }
|
||||
|
||||
[Outlet]
|
||||
[GeneratedCode ("iOS Designer", "1.0")]
|
||||
AVCustomEdit.PlayerView playerView { get; set; }
|
||||
|
||||
[Outlet]
|
||||
[GeneratedCode ("iOS Designer", "1.0")]
|
||||
UIKit.UIBarButtonItem playPauseButton { get; set; }
|
||||
|
||||
[Outlet]
|
||||
[GeneratedCode ("iOS Designer", "1.0")]
|
||||
UIKit.UISlider scrubber { get; set; }
|
||||
|
||||
[Outlet]
|
||||
[GeneratedCode ("iOS Designer", "1.0")]
|
||||
UIKit.UIToolbar toolbar { get; set; }
|
||||
|
||||
[Outlet]
|
||||
[GeneratedCode ("iOS Designer", "1.0")]
|
||||
UIKit.UIBarButtonItem transitionButton { get; set; }
|
||||
|
||||
[Action ("BeginScrubbing:")]
|
||||
[GeneratedCode ("iOS Designer", "1.0")]
|
||||
partial void BeginScrubbing (UIKit.UISlider sender);
|
||||
|
||||
[Action ("EndScrubbing:")]
|
||||
[GeneratedCode ("iOS Designer", "1.0")]
|
||||
partial void EndScrubbing (UIKit.UISlider sender);
|
||||
|
||||
[Action ("ExportToMovie:")]
|
||||
[GeneratedCode ("iOS Designer", "1.0")]
|
||||
partial void ExportToMovie (UIKit.UIBarButtonItem sender);
|
||||
|
||||
[Action ("HandleTapGesture:")]
|
||||
[GeneratedCode ("iOS Designer", "1.0")]
|
||||
partial void HandleTapGesture (UIKit.UITapGestureRecognizer sender);
|
||||
|
||||
[Action ("Scrub:")]
|
||||
[GeneratedCode ("iOS Designer", "1.0")]
|
||||
partial void Scrub (UIKit.UISlider sender);
|
||||
|
||||
[Action ("TogglePlayPause:")]
|
||||
[GeneratedCode ("iOS Designer", "1.0")]
|
||||
partial void TogglePlayPause (UIKit.UIBarButtonItem sender);
|
||||
|
||||
void ReleaseDesignerOutlets ()
|
||||
{
|
||||
if (currentTimeLabel != null) {
|
||||
currentTimeLabel.Dispose ();
|
||||
currentTimeLabel = null;
|
||||
}
|
||||
|
||||
if (exportButton != null) {
|
||||
exportButton.Dispose ();
|
||||
exportButton = null;
|
||||
}
|
||||
|
||||
if (exportProgressView != null) {
|
||||
exportProgressView.Dispose ();
|
||||
exportProgressView = null;
|
||||
}
|
||||
|
||||
if (playerView != null) {
|
||||
playerView.Dispose ();
|
||||
playerView = null;
|
||||
}
|
||||
|
||||
if (playPauseButton != null) {
|
||||
playPauseButton.Dispose ();
|
||||
playPauseButton = null;
|
||||
}
|
||||
|
||||
if (scrubber != null) {
|
||||
scrubber.Dispose ();
|
||||
scrubber = null;
|
||||
}
|
||||
|
||||
if (toolbar != null) {
|
||||
toolbar.Dispose ();
|
||||
toolbar = null;
|
||||
}
|
||||
|
||||
if (transitionButton != null) {
|
||||
transitionButton.Dispose ();
|
||||
transitionButton = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<SampleMetadata>
|
||||
<ID>1ff461ca-3867-43a7-9afb-2a85f08e015b</ID>
|
||||
<IsFullApplication>true</IsFullApplication>
|
||||
<Brief>It's a simple AVFoundation based movie editing application demonstrating custom compositing to add transitions. It implements the CustomVideoCompositor and CustomVideoCompositionInstruction protocols to have access to individual source frames, which are then be rendered using OpenGL off screen rendering.</Brief>
|
||||
<SupportedPlatforms>iOS</SupportedPlatforms>
|
||||
<Level>Intermediate</Level>
|
||||
<Tags>AVFoundation, OpenGL</Tags>
|
||||
<Gallery>true</Gallery>
|
||||
</SampleMetadata>
|
|
@ -5,6 +5,8 @@ AVCustomEdit is a simple AVFoundation based movie editing application demonstrat
|
|||
|
||||
This is a port of Apple's iOS7 sample AVCustomEdit.
|
||||
|
||||
![Home View](Screenshots/screenshot-1.png)
|
||||
|
||||
Instructions
|
||||
------------
|
||||
1. Open the transition menu in the lower right. Select a transition.
|
||||
|
@ -15,8 +17,12 @@ Build
|
|||
------------
|
||||
Building this sample requires Xcode 5.0 and iOS 7 or later.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Xamarin port changes are released under the MIT license.
|
||||
|
||||
Author
|
||||
------------
|
||||
Copyright © 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
Ported to Xamarin.iOS by Timothy Risi.
|
||||
Ported to Xamarin.iOS by Timothy Risi/Mykyta Bondarenko.
|
||||
|
|
После Ширина: | Высота: | Размер: 392 KiB |
После Ширина: | Высота: | Размер: 23 KiB |