// %BANNER_BEGIN%
// ---------------------------------------------------------------------
// %COPYRIGHT_BEGIN%
// Copyright (c) (2018-2022) Magic Leap, Inc. All Rights Reserved.
// Use of this file is governed by the Software License Agreement, located here: https://www.magicleap.com/software-license-agreement-ml2
// Terms and conditions applicable to third-party materials accompanying this distribution may also be found in the top-level NOTICE file appearing herein.
// %COPYRIGHT_END%
// ---------------------------------------------------------------------
// %BANNER_END%
namespace UnityEngine.XR.MagicLeap
{
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
///
/// MLMedia APIs.
///
public partial class MLMedia
{
///
/// MediaMuxer description goes here.
///
public sealed partial class Muxer
{
///
/// See ml_media_muxer.h for additional comments.
///
private class NativeBindings : Native.MagicLeapNativeBindings
{
///
/// Data type that encapsulates sample data to be written in to .
///
[StructLayout(LayoutKind.Sequential)]
public struct MLMediaMuxerSampleData
{
///
/// Version of this structure.
///
public uint Version;
///
/// The track index number, to which to write the sample data into. This should be a value returned by or
///
public uint TrackIndex;
///
/// Pointer to the sample buffer/data.
///
public byte[] Buffer;
///
/// Size of the sample buuffer/data.
///
public uint Size;
///
/// The buffer's time stamp in microseconds.
///
public ulong Time;
///
/// The flag about the data.
///
public CodecBufferFlags Flags;
public static MLMediaMuxerSampleData Create()
{
return new MLMediaMuxerSampleData()
{
Version = 1,
TrackIndex = 0,
Buffer = null,
Size = 0,
Time = 0,
Flags = CodecBufferFlags.KeyFrame
};
}
};
///
/// Create a Muxer
///
[DllImport(MediaMuxerDll, CallingConvention = CallingConvention.Cdecl)]
public static extern MLResult.Code MLMediaMuxerCreate(out ulong muxerHandle);
///
/// Writes an encoded sample into the muxer. The application needs to make sure that the samples are written into the right
/// tracks. Also, it needs to make sure the samples for each track are written in chronological order (e. g. in the
/// order they are provided by the encoder. ) For MPEG4 media format, the duration of the last sample in a track can be set by
/// passing an additional empty buffer) with #MLMediaCodecBufferFlag_EOS flag and a suitable presentation timestamp set in
/// time_us (of MLMediaMuxerSampleData structure) as the last sample of that track. This last sample's presentation
/// timestamp shall be a sum of the presentation timestamp and the duration preferred for the original last sample. If no explicit
/// END_OF_STREAM sample was passed, then the duration of the last sample would be the same as that of the sample before
/// that. The buffer can be reused once this method returns.
///
[DllImport(MediaMuxerDll, CallingConvention = CallingConvention.Cdecl)]
public static extern MLResult.Code MLMediaMuxerWriteSampleData(ulong muxerHandle, in MLMediaMuxerSampleData Data);
///
/// Set the orientation hint for output video playback. This should be called before #MLMediaMuxerStart() and after
/// #MLMediaMuxerConfigure. Calling this method will not rotate the video frame when muxer is generating the file, but add a
/// composition matrix containing the rotation angle in the output video if the output format is MLMediaMuxerOutputFormat_MPEG4
/// so that a video player can choose the proper orientation for playback. Note that some video players may choose to ignore
/// the composition matrix in a video during playback. By default, the rotation degree is 0.
///
[DllImport(MediaMuxerDll, CallingConvention = CallingConvention.Cdecl)]
public static extern MLResult.Code MLMediaMuxerSetOrientationHint(ulong muxerHandle, int Degrees);
///
/// Stop muxing. Once the muxer stops, it can not be restarted.
///
[DllImport(MediaMuxerDll, CallingConvention = CallingConvention.Cdecl)]
public static extern MLResult.Code MLMediaMuxerStop(ulong muxerHandle);
///
/// Start muxing. Make sure all the tracks have been added (#MLMediaMuxerAddTrack) before calling this.
///
[DllImport(MediaMuxerDll, CallingConvention = CallingConvention.Cdecl)]
public static extern MLResult.Code MLMediaMuxerStart(ulong muxerHandle);
///
/// Release the Muxer
///
[DllImport(MediaMuxerDll, CallingConvention = CallingConvention.Cdecl)]
public static extern MLResult.Code MLMediaMuxerRelease(ulong muxerHandle);
///
/// Get a list of all the supported Media Muxer Output Formats on the Platform. User doesn't own the memory/buffer returned
/// from this API and MUST NOT be freeing or releasing the out_format_list.
///
[DllImport(MediaMuxerDll, CallingConvention = CallingConvention.Cdecl)]
public static extern MLResult.Code MLMediaMuxerGetSupportedOutputFormats(ulong muxerHandle, [MarshalAs(UnmanagedType.LPArray)] out IntPtr OutFormatList, out int OutFormatListSize);
///
/// Get a list of all the supported mime-types for a given Media Muxer Output Format on the Platform. User doesn't own the
/// memory/buffer returned from this API and MUST NOT be freeing or releasing the out_mime_list.
///
[DllImport(MediaMuxerDll, CallingConvention = CallingConvention.Cdecl)]
public static extern MLResult.Code MLMediaMuxerGetSupportedMimes(ulong muxerHandle, OutputFormat Format, out IntPtr OutMimeList, out int OutMimeListSize);
///
/// Add a track with given format information.
///
[DllImport(MediaMuxerDll, CallingConvention = CallingConvention.Cdecl)]
public static extern MLResult.Code MLMediaMuxerAddTrack(ulong muxerHandle, ulong formatHandle, out UIntPtr OutTrackIndex);
///
/// Configure the Muxer
///
[DllImport(MediaMuxerDll, CallingConvention = CallingConvention.Cdecl)]
public static extern MLResult.Code MLMediaMuxerConfigure(ulong muxerHandle, int Format, string Path);
///
/// Set and store the geodata (latitude and longitude) in the output file. This should be called before
/// #MLMediaMuxerStart() and after #MLMediaMuxerConfigure. The geodata is stored in udta box if the output format is
/// MLMediaMuxerOutputFormat_MPEG4, and is ignored for other output formats. The geodata is stored according to ISO-6709 standard.
///
[DllImport(MediaMuxerDll, CallingConvention = CallingConvention.Cdecl)]
public static extern MLResult.Code MLMediaMuxerSetLocation(ulong muxerHandle, float Latitude, float Longitude);
}
private MLResult.Code InternalGetSupportedOutputFormats(out OutputFormat[] formats)
{
const int sizeOfOutputFormatEnum = sizeof(OutputFormat);
var resultCode = NativeBindings.MLMediaMuxerGetSupportedOutputFormats(handle, out IntPtr resultPtr, out int resultCount);
MLResult.DidNativeCallSucceed(resultCode, nameof(NativeBindings.MLMediaMuxerGetSupportedOutputFormats));
formats = new OutputFormat[resultCount];
if (resultCode == MLResult.Code.Ok)
{
for (int i = 0; i < resultCount; i++)
{
formats[i] = (OutputFormat)Marshal.ReadInt32(resultPtr);
resultPtr += sizeOfOutputFormatEnum;
}
}
Marshal.FreeHGlobal(resultPtr);
return resultCode;
}
private MLResult.Code InternalGetSupportedMimeTypes(OutputFormat format, out string[] mimeResults)
{
var resultCode = NativeBindings.MLMediaMuxerGetSupportedMimes(handle, format, out IntPtr resultsPtr, out int resultsCount);
MLResult.DidNativeCallSucceed(resultCode, nameof(NativeBindings.MLMediaMuxerGetSupportedMimes));
mimeResults = new string[resultsCount];
if (resultCode == MLResult.Code.Ok)
{
IntPtr[] pIntPtrArray = new IntPtr[resultsCount];
Marshal.Copy(resultsPtr, pIntPtrArray, 0, resultsCount);
for (int i = 0; i < resultsCount; i++)
{
mimeResults[i] = Marshal.PtrToStringAnsi(pIntPtrArray[i]);
}
}
Marshal.FreeCoTaskMem(resultsPtr);
return resultCode;
}
private MLResult.Code InternalAddTrack(ulong mediaFormatHandle, out int trackIndex)
{
var resultCode = NativeBindings.MLMediaMuxerAddTrack(handle, mediaFormatHandle, out UIntPtr indexPtr);
trackIndex = (int)indexPtr;
MLResult.DidNativeCallSucceed(resultCode, nameof(NativeBindings.MLMediaMuxerAddTrack));
return resultCode;
}
private MLResult.Code InternalWriteSampleData(int trackIndex, byte[] buffer, long time, CodecBufferFlags flags)
{
var sampleData = NativeBindings.MLMediaMuxerSampleData.Create();
sampleData.TrackIndex = (uint)trackIndex;
sampleData.Buffer = buffer;
sampleData.Size = (buffer == null) ? 0 : (uint)buffer.Length;
sampleData.Time = (ulong)time;
sampleData.Flags = flags;
var resultCode = NativeBindings.MLMediaMuxerWriteSampleData(handle, sampleData);
MLResult.DidNativeCallSucceed(resultCode, nameof(NativeBindings.MLMediaMuxerWriteSampleData));
return resultCode;
}
}
}
}