Initial commit

This commit is contained in:
Thastertyn committed 2024-06-07 00:47:07 +02:00
commit 262601edb8
2025 files changed
+218754

No files matched your search

+327
View File
@@ -0,0 +1,327 @@
//
// AudioFile.cs: Provides tagging and properties support for MPEG-1, MPEG-2, and
// MPEG-2.5 audio files.
//
// Author:
// Brian Nickel (brian.nickel@gmail.com)
//
// Original Source:
// mpegfile.cpp from TagLib
//
// Copyright (C) 2005-2007 Brian Nickel
// Copyright (C) 2002, 2003 by Scott Wheeler (Original Implementation)
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License version
// 2.1 as published by the Free Software Foundation.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA
//
using System;
namespace TagLib.Mpeg
{
/// <summary>
/// This class extends <see cref="TagLib.NonContainer.File" /> to
/// provide tagging and properties support for MPEG-1, MPEG-2, and
/// MPEG-2.5 audio files.
/// </summary>
/// <remarks>
/// A <see cref="TagLib.Id3v1.Tag" /> and <see
/// cref="TagLib.Id3v2.Tag" /> will be added automatically to any
/// file that doesn't contain one. This change does not effect the
/// file until it is saved and can be reversed using the following
/// method:
/// <code>file.RemoveTags (file.TagTypes &amp; ~file.TagTypesOnDisk);</code>
/// </remarks>
[SupportedMimeType ("taglib/mp3", "mp3")]
[SupportedMimeType ("audio/x-mp3")]
[SupportedMimeType ("application/x-id3")]
[SupportedMimeType ("audio/mpeg")]
[SupportedMimeType ("audio/x-mpeg")]
[SupportedMimeType ("audio/x-mpeg-3")]
[SupportedMimeType ("audio/mpeg3")]
[SupportedMimeType ("audio/mp3")]
[SupportedMimeType ("taglib/m2a", "m2a")]
[SupportedMimeType ("taglib/mp2", "mp2")]
[SupportedMimeType ("taglib/mp1", "mp1")]
[SupportedMimeType ("audio/x-mp2")]
[SupportedMimeType ("audio/x-mp1")]
public class AudioFile : TagLib.NonContainer.File
{
#region Private Fields
/// <summary>
/// Contains the first audio header.
/// </summary>
AudioHeader first_header;
#endregion
#region Private Static Fields
/// <summary>
/// Specifies whether or not to create ID3v1 and
/// ID3v2 tags when they don't exist..
/// </summary>
private static bool create_id3_tags = true;
#endregion
#region Constructors
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="AudioFile" /> for a specified path in the local
/// file system and specified read style.
/// </summary>
/// <param name="path">
/// A <see cref="string" /> object containing the path of the
/// file to use in the new instance.
/// </param>
/// <param name="propertiesStyle">
/// A <see cref="ReadStyle" /> value specifying at what level
/// of accuracy to read the media properties, or <see
/// cref="ReadStyle.None" /> to ignore the properties.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="path" /> is <see langword="null" />.
/// </exception>
public AudioFile (string path, ReadStyle propertiesStyle)
: base (path, propertiesStyle)
{
}
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="AudioFile" /> for a specified path in the local
/// file system with an average read style.
/// </summary>
/// <param name="path">
/// A <see cref="string" /> object containing the path of the
/// file to use in the new instance.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="path" /> is <see langword="null" />.
/// </exception>
public AudioFile (string path)
: base (path)
{
}
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="AudioFile" /> for a specified file abstraction and
/// specified read style.
/// </summary>
/// <param name="abstraction">
/// A <see cref="TagLib.File.IFileAbstraction" /> object to use when
/// reading from and writing to the file.
/// </param>
/// <param name="propertiesStyle">
/// A <see cref="ReadStyle" /> value specifying at what level
/// of accuracy to read the media properties, or <see
/// cref="ReadStyle.None" /> to ignore the properties.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="abstraction" /> is <see langword="null"
/// />.
/// </exception>
public AudioFile (IFileAbstraction abstraction, ReadStyle propertiesStyle)
: base (abstraction, propertiesStyle)
{
}
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="AudioFile" /> for a specified file abstraction with
/// an average read style.
/// </summary>
/// <param name="abstraction">
/// A <see cref="TagLib.File.IFileAbstraction" /> object to use when
/// reading from and writing to the file.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="abstraction" /> is <see langword="null"
/// />.
/// </exception>
public AudioFile (IFileAbstraction abstraction)
: base (abstraction)
{
}
#endregion
#region Public Static Properties
/// <summary>
/// Gets and sets whether or not to create ID3v1 and
/// ID3v2 tags automatically when they are not existing.
/// </summary>
/// <value>
/// <see langword="true" /> if tags to be created automatically.
/// Otherwise, <see langword="false" />.
/// </value>
/// <remarks>
/// <para>Sometimes a MP3 file should only contain ID3v1 and no
/// ID3v2 Tags. Or instead of ID3v2 Tags APE Tags should be used.
/// By setting this property to <see langword="false" />,
/// no ID3v1 and Id3v2 Tags will be created when creating the file,
/// if they don't exist.
/// They need to be created explicitly if needed.</para>
/// <para>The default is <see langword="true" /> which means that
/// ID3v1 and Id3v2 tags are created when they don't exist.</para>
/// </remarks>
public static bool CreateID3Tags {
get { return create_id3_tags; }
set { create_id3_tags = value; }
}
#endregion
#region Public Methods
/// <summary>
/// Gets a tag of a specified type from the current instance,
/// optionally creating a new tag if possible.
/// </summary>
/// <param name="type">
/// A <see cref="TagLib.TagTypes" /> value indicating the
/// type of tag to read.
/// </param>
/// <param name="create">
/// A <see cref="bool" /> value specifying whether or not to
/// try and create the tag if one is not found.
/// </param>
/// <returns>
/// A <see cref="Tag" /> object containing the tag that was
/// found in or added to the current instance. If no
/// matching tag was found and none was created, <see
/// langword="null" /> is returned.
/// </returns>
/// <remarks>
/// If a <see cref="TagLib.Id3v2.Tag" /> is added to the
/// current instance, it will be placed at the start of the
/// file. On the other hand, <see cref="TagLib.Id3v1.Tag" />
/// <see cref="TagLib.Ape.Tag" /> will be added to the end of
/// the file. All other tag types will be ignored.
/// </remarks>
public override Tag GetTag (TagTypes type, bool create)
{
Tag t = (Tag as TagLib.NonContainer.Tag).GetTag (type);
if (t != null || !create)
return t;
switch (type) {
case TagTypes.Id3v1:
return EndTag.AddTag (type, Tag);
case TagTypes.Id3v2:
return StartTag.AddTag (type, Tag);
case TagTypes.Ape:
return EndTag.AddTag (type, Tag);
default:
return null;
}
}
#endregion
#region Protected Methods
/// <summary>
/// Reads format specific information at the start of the
/// file.
/// </summary>
/// <param name="start">
/// A <see cref="long" /> value containing the seek position
/// at which the tags end and the media data begins.
/// </param>
/// <param name="propertiesStyle">
/// A <see cref="ReadStyle" /> value specifying at what level
/// of accuracy to read the media properties, or <see
/// cref="ReadStyle.None" /> to ignore the properties.
/// </param>
/// <remarks>
/// This method only searches for an audio header in the
/// first 16384 bytes of code to avoid searching forever in
/// corrupt files.
/// </remarks>
protected override void ReadStart (long start, ReadStyle propertiesStyle)
{
// Only check the first 16 bytes so we're not stuck
// reading a bad file forever.
if ((propertiesStyle & ReadStyle.Average) != 0 &&
!AudioHeader.Find (out first_header, this, start, 0x4000))
throw new CorruptFileException ("MPEG audio header not found.");
}
/// <summary>
/// Reads format specific information at the end of the
/// file.
/// </summary>
/// <param name="end">
/// A <see cref="long" /> value containing the seek position
/// at which the media data ends and the tags begin.
/// </param>
/// <param name="propertiesStyle">
/// A <see cref="ReadStyle" /> value specifying at what level
/// of accuracy to read the media properties, or <see
/// cref="ReadStyle.None" /> to ignore the properties.
/// </param>
protected override void ReadEnd (long end, ReadStyle propertiesStyle)
{
// Creation of ID3v1 and ID3v2 tags based on CreateID3Tags property
GetTag (TagTypes.Id3v1, create_id3_tags);
GetTag (TagTypes.Id3v2, create_id3_tags);
}
/// <summary>
/// Reads the audio properties from the file represented by
/// the current instance.
/// </summary>
/// <param name="start">
/// A <see cref="long" /> value containing the seek position
/// at which the tags end and the media data begins.
/// </param>
/// <param name="end">
/// A <see cref="long" /> value containing the seek position
/// at which the media data ends and the tags begin.
/// </param>
/// <param name="propertiesStyle">
/// A <see cref="ReadStyle" /> value specifying at what level
/// of accuracy to read the media properties, or <see
/// cref="ReadStyle.None" /> to ignore the properties.
/// </param>
/// <returns>
/// A <see cref="TagLib.Properties" /> object describing the
/// media properties of the file represented by the current
/// instance.
/// </returns>
protected override Properties ReadProperties (long start, long end, ReadStyle propertiesStyle)
{
first_header.SetStreamLength (end - start);
return new Properties (TimeSpan.Zero, first_header);
}
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d1e35bf1994ce3b83961028aba3d0aa7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+777
View File
@@ -0,0 +1,777 @@
//
// AudioHeader.cs: Provides information about an MPEG audio stream.
//
// Author:
// Brian Nickel (brian.nickel@gmail.com)
//
// Original Source:
// mpegheader.cpp from TagLib
//
// Copyright (C) 2005-2007 Brian Nickel
// Copyright (C) 2003 by Scott Wheeler (Original Implementation)
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License version
// 2.1 as published by the Free Software Foundation.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA
//
using System;
namespace TagLib.Mpeg
{
#region Enums
/// <summary>
/// Indicates the MPEG version of a file or stream.
/// </summary>
public enum Version
{
/// <summary>
/// Unknown version.
/// </summary>
Unknown = -1,
/// <summary>
/// MPEG-1
/// </summary>
Version1 = 0,
/// <summary>
/// MPEG-2
/// </summary>
Version2 = 1,
/// <summary>
/// MPEG-2.5
/// </summary>
Version25 = 2
}
/// <summary>
/// Indicates the MPEG audio channel mode of a file or stream.
/// </summary>
public enum ChannelMode
{
/// <summary>
/// Stereo
/// </summary>
Stereo = 0,
/// <summary>
/// Joint Stereo
/// </summary>
JointStereo = 1,
/// <summary>
/// Dual Channel Mono
/// </summary>
DualChannel = 2,
/// <summary>
/// Single Channel Mono
/// </summary>
SingleChannel = 3
}
#endregion
/// <summary>
/// This structure implements <see cref="IAudioCodec" /> and provides
/// information about an MPEG audio stream.
/// </summary>
public struct AudioHeader : IAudioCodec
{
#region Private Static Value Arrays
/// <summary>
/// Contains a sample rate table for MPEG audio.
/// </summary>
static readonly int[,] sample_rates = new int[3, 4] {
{44100, 48000, 32000, 0}, // Version 1
{22050, 24000, 16000, 0}, // Version 2
{11025, 12000, 8000, 0} // Version 2.5
};
/// <summary>
/// Contains a block size table for MPEG audio.
/// </summary>
static readonly int[,] block_size = new int[3, 4] {
{0, 384, 1152, 1152}, // Version 1
{0, 384, 1152, 576}, // Version 2
{0, 384, 1152, 576} // Version 2.5
};
/// <summary>
/// Contains a bitrate table for MPEG audio.
/// </summary>
static readonly int[,,] bitrates = new int[2, 3, 16] {
{ // Version 1
{0, 32, 64, 96, 128, 160, 192, 224, 256, 288,
320, 352, 384, 416, 448, -1}, // layer 1
{0, 32, 48, 56, 64, 80, 96, 112, 128, 160,
192, 224, 256, 320, 384, -1}, // layer 2
{0, 32, 40, 48, 56, 64, 80, 96, 112, 128,
160, 192, 224, 256, 320, -1} // layer 3
},
{ // Version 2 or 2.5
{0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160,
176, 192, 224, 256, -1}, // layer 1
{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,
112, 128, 144, 160, -1}, // layer 2
{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,
112, 128, 144, 160, -1} // layer 3
}
};
#endregion
#region Private Properties
/// <summary>
/// Contains the header flags.
/// </summary>
readonly uint flags;
/// <summary>
/// Contains the audio stream length.
/// </summary>
long stream_length;
/// <summary>
/// Contains the associated Xing header.
/// </summary>
XingHeader xing_header;
/// <summary>
/// Contains the associated VBRI header.
/// </summary>
readonly VBRIHeader vbri_header;
/// <summary>
/// Contains the audio stream duration.
/// </summary>
TimeSpan duration;
#endregion
#region Public Fields
/// <summary>
/// An empty and unset header.
/// </summary>
public static readonly AudioHeader Unknown =
new AudioHeader (0, 0, XingHeader.Unknown, VBRIHeader.Unknown);
#endregion
#region Constructors
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="AudioHeader" /> by populating it with specified
/// values.
/// </summary>
/// <param name="flags">
/// A <see cref="uint" /> value specifying flags for the new
/// instance.
/// </param>
/// <param name="streamLength">
/// A <see cref="long" /> value specifying the stream length
/// of the new instance.
/// </param>
/// <param name="xingHeader">
/// A <see cref="XingHeader" /> object representing the Xing
/// header associated with the new instance.
/// </param>
/// <param name="vbriHeader">
/// A <see cref="VBRIHeader" /> object representing the VBRI
/// header associated with the new instance.
/// </param>
AudioHeader (uint flags, long streamLength, XingHeader xingHeader, VBRIHeader vbriHeader)
{
this.flags = flags;
stream_length = streamLength;
xing_header = xingHeader;
vbri_header = vbriHeader;
duration = TimeSpan.Zero;
}
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="AudioHeader" /> by reading its contents from a
/// <see cref="ByteVector" /> object and its Xing Header from
/// the appropriate location in the specified file.
/// </summary>
/// <param name="data">
/// A <see cref="ByteVector" /> object containing the header
/// to read.
/// </param>
/// <param name="file">
/// A <see cref="TagLib.File" /> object to read the Xing
/// header from.
/// </param>
/// <param name="position">
/// A <see cref="long" /> value indicating the position in
/// <paramref name="file" /> at which the header begins.
/// </param>
/// <exception cref="CorruptFileException">
/// <paramref name="data" /> is less than 4 bytes long,
/// does not begin with a MPEG audio synch, has a negative
/// bitrate, or has a sample rate of zero.
/// </exception>
AudioHeader (ByteVector data, TagLib.File file, long position)
{
duration = TimeSpan.Zero;
stream_length = 0;
string error = GetHeaderError (data);
if (error != null) {
throw new CorruptFileException (error);
}
flags = data.ToUInt ();
xing_header = XingHeader.Unknown;
vbri_header = VBRIHeader.Unknown;
// Check for a Xing header that will help us in
// gathering information about a VBR stream.
file.Seek (position + XingHeader.XingHeaderOffset (Version, ChannelMode));
ByteVector xing_data = file.ReadBlock (16);
if (xing_data.Count == 16 && xing_data.StartsWith (XingHeader.FileIdentifier))
xing_header = new XingHeader (xing_data);
if (xing_header.Present)
return;
// A Xing header could not be found, next chec for a
// Fraunhofer VBRI header.
file.Seek (position + VBRIHeader.VBRIHeaderOffset ());
// Only get the first 24 bytes of the Header.
// We're not interested in the TOC entries.
ByteVector vbri_data = file.ReadBlock (24);
if (vbri_data.Count == 24 &&
vbri_data.StartsWith (VBRIHeader.FileIdentifier))
vbri_header = new VBRIHeader (vbri_data);
}
#endregion
#region Public Properties
/// <summary>
/// Gets the MPEG version used to encode the audio
/// represented by the current instance.
/// </summary>
/// <value>
/// A <see cref="Version" /> value indicating the MPEG
/// version used to encode the audio represented by the
/// current instance.
/// </value>
public Version Version {
get {
switch ((flags >> 19) & 0x03) {
case 0:
return Version.Version25;
case 2:
return Version.Version2;
default:
return Version.Version1;
}
}
}
/// <summary>
/// Gets the MPEG audio layer used to encode the audio
/// represented by the current instance.
/// </summary>
/// <value>
/// A <see cref="int" /> value indicating the MPEG audio
/// layer used to encode the audio represented by the current
/// instance.
/// </value>
public int AudioLayer {
get {
switch ((flags >> 17) & 0x03) {
case 1:
return 3;
case 2:
return 2;
default:
return 1;
}
}
}
/// <summary>
/// Gets the bitrate of the audio represented by the current
/// instance.
/// </summary>
/// <value>
/// A <see cref="int" /> value containing a bitrate of the
/// audio represented by the current instance.
/// </value>
public int AudioBitrate {
get {
if (xing_header.TotalSize > 0 &&
xing_header.TotalFrames > 0 &&
Duration > TimeSpan.Zero)
return (int)Math.Round (((
(XingHeader.TotalSize * 8L) /
Duration.TotalSeconds) / 1000.0));
if (vbri_header.TotalSize > 0 &&
vbri_header.TotalFrames > 0 &&
Duration > TimeSpan.Zero)
return (int)Math.Round (((
(VBRIHeader.TotalSize * 8L) /
Duration.TotalSeconds) / 1000.0));
return bitrates[
Version == Version.Version1 ? 0 : 1,
AudioLayer > 0 ? AudioLayer - 1 : 0,
(int)(flags >> 12) & 0x0F];
}
}
/// <summary>
/// Gets the sample rate of the audio represented by the
/// current instance.
/// </summary>
/// <value>
/// A <see cref="int" /> value containing the sample rate of
/// the audio represented by the current instance.
/// </value>
public int AudioSampleRate {
get {
return sample_rates[(int)Version,
(int)(flags >> 10) & 0x03];
}
}
/// <summary>
/// Gets the number of channels in the audio represented by
/// the current instance.
/// </summary>
/// <value>
/// A <see cref="int" /> value containing the number of
/// channels in the audio represented by the current
/// instance.
/// </value>
public int AudioChannels {
get { return ChannelMode == ChannelMode.SingleChannel ? 1 : 2; }
}
/// <summary>
/// Gets the length of the frames in the audio represented by
/// the current instance.
/// </summary>
/// <value>
/// A <see cref="int" /> value containing the length of the
/// frames in the audio represented by the current instance.
/// </value>
public int AudioFrameLength {
get {
switch (AudioLayer) {
case 1:
return 48000 * AudioBitrate /
AudioSampleRate +
(IsPadded ? 4 : 0);
case 2:
return 144000 * AudioBitrate /
AudioSampleRate +
(IsPadded ? 1 : 0);
case 3:
if (Version == Version.Version1)
goto case 2;
return 72000 * AudioBitrate /
AudioSampleRate +
(IsPadded ? 1 : 0);
default: return 0;
}
}
}
/// <summary>
/// Gets the duration of the media represented by the current
/// instance.
/// </summary>
/// <value>
/// A <see cref="TimeSpan" /> containing the duration of the
/// media represented by the current instance.
/// </value>
/// <remarks>
/// If <see cref="XingHeader" /> is equal to <see
/// cref="XingHeader.Unknown" /> and <see
/// cref="SetStreamLength" /> has not been called, this value
/// will not be correct.
/// If <see cref="VBRIHeader" /> is equal to <see
/// cref="VBRIHeader.Unknown" /> and <see
/// cref="SetStreamLength" /> has not been called, this value
/// will not be correct.
/// </remarks>
public TimeSpan Duration {
get {
if (duration > TimeSpan.Zero)
return duration;
if (xing_header.TotalFrames > 0) {
// Read the length and the bitrate from
// the Xing header.
double time_per_frame = block_size[(int)Version,
AudioLayer] / (double)
AudioSampleRate;
duration = TimeSpan.FromSeconds (
time_per_frame *
XingHeader.TotalFrames);
} else if (vbri_header.TotalFrames > 0) {
// Read the length and the bitrate from
// the VBRI header.
double time_per_frame =
block_size[(int)Version, AudioLayer] / (double)AudioSampleRate;
duration = TimeSpan.FromSeconds (Math.Round (time_per_frame * VBRIHeader.TotalFrames));
} else if (AudioFrameLength > 0 &&
AudioBitrate > 0) {
// Since there was no valid Xing or VBRI
// header found, we hope that we're in a
// constant bitrate file.
// Round off to upper integer value
int frames = (int)((stream_length + AudioFrameLength - 1) / AudioFrameLength);
duration = TimeSpan.FromSeconds ( (double)(AudioFrameLength * frames) / (AudioBitrate * 125));
}
return duration;
}
}
/// <summary>
/// Gets a text description of the media represented by the
/// current instance.
/// </summary>
/// <value>
/// A <see cref="string" /> object containing a description
/// of the media represented by the current instance.
/// </value>
public string Description {
get {
var builder = new System.Text.StringBuilder ();
builder.Append ("MPEG Version ");
switch (Version) {
case Version.Version1:
builder.Append ("1");
break;
case Version.Version2:
builder.Append ("2");
break;
case Version.Version25:
builder.Append ("2.5");
break;
}
builder.Append (" Audio, Layer ");
builder.Append (AudioLayer);
if (xing_header.Present || vbri_header.Present)
builder.Append (" VBR");
return builder.ToString ();
}
}
/// <summary>
/// Gets the types of media represented by the current
/// instance.
/// </summary>
/// <value>
/// Always <see cref="MediaTypes.Audio" />.
/// </value>
public MediaTypes MediaTypes {
get { return MediaTypes.Audio; }
}
/// <summary>
/// Gets whether or not the audio represented by the current
/// instance is protected.
/// </summary>
/// <value>
/// A <see cref="bool" /> value indicating whether or not the
/// audio represented by the current instance is protected.
/// </value>
public bool IsProtected {
get { return ((flags >> 16) & 1) == 0; }
}
/// <summary>
/// Gets whether or not the audio represented by the current
/// instance is padded.
/// </summary>
/// <value>
/// A <see cref="bool" /> value indicating whether or not the
/// audio represented by the current instance is padded.
/// </value>
public bool IsPadded {
get { return ((flags >> 9) & 1) == 1; }
}
/// <summary>
/// Gets whether or not the audio represented by the current
/// instance is copyrighted.
/// </summary>
/// <value>
/// A <see cref="bool" /> value indicating whether or not the
/// audio represented by the current instance is copyrighted.
/// </value>
public bool IsCopyrighted {
get { return ((flags >> 3) & 1) == 1; }
}
/// <summary>
/// Gets whether or not the audio represented by the current
/// instance is original.
/// </summary>
/// <value>
/// A <see cref="bool" /> value indicating whether or not the
/// audio represented by the current instance is original.
/// </value>
public bool IsOriginal {
get { return ((flags >> 2) & 1) == 1; }
}
/// <summary>
/// Gets the MPEG audio channel mode of the audio represented
/// by the current instance.
/// </summary>
/// <value>
/// A <see cref="ChannelMode" /> value indicating the MPEG
/// audio channel mode of the audio represented by the
/// current instance.
/// </value>
public ChannelMode ChannelMode {
get { return (ChannelMode)((flags >> 6) & 0x03); }
}
/// <summary>
/// Gets the Xing header found in the audio represented by
/// the current instance.
/// </summary>
/// <value>
/// A <see cref="XingHeader" /> object containing the Xing
/// header found in the audio represented by the current
/// instance, or <see cref="XingHeader.Unknown" /> if no
/// header was found.
/// </value>
public XingHeader XingHeader {
get { return xing_header; }
}
/// <summary>
/// Gets the VBRI header found in the audio represented by
/// the current instance.
/// </summary>
/// <value>
/// A <see cref="VBRIHeader" /> object containing the VBRI
/// header found in the audio represented by the current
/// instance, or <see cref="VBRIHeader.Unknown" /> if no
/// header was found.
/// </value>
public VBRIHeader VBRIHeader {
get { return vbri_header; }
}
#endregion
#region Public Methods
/// <summary>
/// Sets the length of the audio stream represented by the
/// current instance.
/// </summary>
/// <param name="streamLength">
/// A <see cref="long" /> value specifying the length in
/// bytes of the audio stream represented by the current
/// instance.
/// </param>
/// <remarks>
/// The this value has been set, <see cref="Duration" /> will
/// return an incorrect value.
/// </remarks>
public void SetStreamLength (long streamLength)
{
stream_length = streamLength;
// Force the recalculation of duration if it depends on
// the stream length.
if (xing_header.TotalFrames == 0 ||
vbri_header.TotalFrames == 0)
duration = TimeSpan.Zero;
}
#endregion
#region Public Static Methods
/// <summary>
/// Searches for an audio header in a <see cref="TagLib.File"
/// /> starting at a specified position and searching through
/// a specified number of bytes.
/// </summary>
/// <param name="header">
/// A <see cref="AudioHeader" /> object in which the found
/// header will be stored.
/// </param>
/// <param name="file">
/// A <see cref="TagLib.File" /> object to search.
/// </param>
/// <param name="position">
/// A <see cref="long" /> value specifying the seek position
/// in <paramref name="file" /> at which to start searching.
/// </param>
/// <param name="length">
/// A <see cref="int" /> value specifying the maximum number
/// of bytes to search before aborting.
/// </param>
/// <returns>
/// A <see cref="bool" /> value indicating whether or not a
/// header was found.
/// </returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="file" /> is <see langword="null" />.
/// </exception>
public static bool Find (out AudioHeader header, TagLib.File file, long position, int length)
{
if (file == null)
throw new ArgumentNullException (nameof (file));
long end = position + length;
header = Unknown;
file.Seek (position);
ByteVector buffer = file.ReadBlock (3);
if (buffer.Count < 3)
return false;
do {
file.Seek (position + 3);
buffer = buffer.Mid (buffer.Count - 3);
buffer.Add (file.ReadBlock (
(int)TagLib.File.BufferSize));
for (int i = 0; i < buffer.Count - 3 &&
(length < 0 || position + i < end); i++)
if (buffer[i] == 0xFF &&
buffer[i + 1] > 0xE0) {
ByteVector data = buffer.Mid (i, 4);
if (GetHeaderError (data) == null) {
try {
header = new AudioHeader (
data,
file, position + i);
return true;
} catch (CorruptFileException) {
}
}
}
position += TagLib.File.BufferSize;
} while (buffer.Count > 3 && (length < 0 || position < end));
return false;
}
/// <summary>
/// Searches for an audio header in a <see cref="TagLib.File"
/// /> starting at a specified position and searching to the
/// end of the file.
/// </summary>
/// <param name="header">
/// A <see cref="AudioHeader" /> object in which the found
/// header will be stored.
/// </param>
/// <param name="file">
/// A <see cref="TagLib.File" /> object to search.
/// </param>
/// <param name="position">
/// A <see cref="long" /> value specifying the seek position
/// in <paramref name="file" /> at which to start searching.
/// </param>
/// <returns>
/// A <see cref="bool" /> value indicating whether or not a
/// header was found.
/// </returns>
/// <remarks>
/// Searching to the end of the file can be very, very slow
/// especially for corrupt or non-MPEG files. It is
/// recommended to use <see
/// cref="M:AudioHeader.Find(AudioHeader,TagLib.File,long,int)" />
/// instead.
/// </remarks>
public static bool Find (out AudioHeader header, TagLib.File file, long position)
{
return Find (out header, file, position, -1);
}
static string GetHeaderError (ByteVector data)
{
if (data.Count < 4)
return "Insufficient header length.";
if (data[0] != 0xFF)
return "First byte did not match MPEG synch.";
// Checking bits from high to low:
//
// First 3 bits MUST be set. Bits 4 and 5 can
// be 00, 10, or 11 but not 01. One or more of
// bits 6 and 7 must be set. Bit 8 can be
// anything.
if ((data[1] & 0xE6) <= 0xE0 || (data[1] & 0x18) == 0x08)
return "Second byte did not match MPEG synch.";
uint flags = data.ToUInt ();
if (((flags >> 12) & 0x0F) == 0x0F)
return "Header uses invalid bitrate index.";
if (((flags >> 10) & 0x03) == 0x03)
return "Invalid sample rate.";
return null;
}
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 43f435ccb204431b0984c09119dfd456
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+689
View File
@@ -0,0 +1,689 @@
//
// File.cs: Provides tagging and properties support for MPEG-1, MPEG-2, and
// MPEG-2.5 audio files.
//
// Author:
// Brian Nickel (brian.nickel@gmail.com)
//
// Copyright (C) 2007 Brian Nickel
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License version
// 2.1 as published by the Free Software Foundation.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA
//
using System;
namespace TagLib.Mpeg
{
/// <summary>
/// Indicates the type of marker found in a MPEG file.
/// </summary>
public enum Marker
{
/// <summary>
/// An invalid marker.
/// </summary>
Corrupt = -1,
/// <summary>
/// A zero value marker.
/// </summary>
Zero = 0,
/// <summary>
/// A marker indicating a system sync packet.
/// </summary>
SystemSyncPacket = 0xBA,
/// <summary>
/// A marker indicating a video sync packet.
/// </summary>
VideoSyncPacket = 0xB3,
/// <summary>
/// A marker indicating a system packet.
/// </summary>
SystemPacket = 0xBB,
/// <summary>
/// A marker indicating a padding packet.
/// </summary>
PaddingPacket = 0xBE,
/// <summary>
/// A marker indicating a audio packet.
/// </summary>
AudioPacket = 0xC0,
/// <summary>
/// A marker indicating a video packet.
/// </summary>
VideoPacket = 0xE0,
/// <summary>
/// A marker indicating the end of a stream.
/// </summary>
EndOfStream = 0xB9
}
/// <summary>
/// This class extends <see cref="TagLib.NonContainer.File" /> to
/// provide tagging and properties support for MPEG-1, MPEG-2, and
/// MPEG-2.5 video files.
/// </summary>
/// <remarks>
/// A <see cref="TagLib.Id3v1.Tag" /> and <see
/// cref="TagLib.Id3v2.Tag" /> will be added automatically to any
/// file that doesn't contain one. This change does not effect the
/// file until it is saved and can be reversed using the following
/// method:
/// <code>file.RemoveTags (file.TagTypes &amp; ~file.TagTypesOnDisk);</code>
/// </remarks>
[SupportedMimeType ("taglib/mpg", "mpg")]
[SupportedMimeType ("taglib/mpeg", "mpeg")]
[SupportedMimeType ("taglib/mpe", "mpe")]
[SupportedMimeType ("taglib/mpv2", "mpv2")]
[SupportedMimeType ("taglib/m2v", "m2v")]
[SupportedMimeType ("video/x-mpg")]
[SupportedMimeType ("video/mpeg")]
public class File : TagLib.NonContainer.File
{
#region Private Static Fields
static readonly ByteVector MarkerStart = new byte[] { 0, 0, 1 };
#endregion
#region Private Fields
/// <summary>
/// Contains the MPEG version.
/// </summary>
Version version;
/// <summary>
/// Contains the first audio header.
/// </summary>
AudioHeader audio_header;
/// <summary>
/// Contains the first video header.
/// </summary>
VideoHeader video_header;
/// <summary>
/// Indicates whether or not audio was found.
/// </summary>
bool video_found;
/// <summary>
/// Indicates whether or not video was found.
/// </summary>
bool audio_found;
/// <summary>
/// Contains the start time of the file.
/// </summary>
double? start_time;
/// <summary>
/// Contains the end time of the file.
/// </summary>
double end_time;
#endregion
#region Constructors
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="File" /> for a specified path in the local file
/// system and specified read style.
/// </summary>
/// <param name="path">
/// A <see cref="string" /> object containing the path of the
/// file to use in the new instance.
/// </param>
/// <param name="propertiesStyle">
/// A <see cref="ReadStyle" /> value specifying at what level
/// of accuracy to read the media properties, or <see
/// cref="ReadStyle.None" /> to ignore the properties.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="path" /> is <see langword="null" />.
/// </exception>
public File (string path, ReadStyle propertiesStyle)
: base (path, propertiesStyle)
{
}
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="File" /> for a specified path in the local file
/// system with an average read style.
/// </summary>
/// <param name="path">
/// A <see cref="string" /> object containing the path of the
/// file to use in the new instance.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="path" /> is <see langword="null" />.
/// </exception>
public File (string path)
: base (path)
{
}
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="File" /> for a specified file abstraction and
/// specified read style.
/// </summary>
/// <param name="abstraction">
/// A <see cref="TagLib.File.IFileAbstraction" /> object to use when
/// reading from and writing to the file.
/// </param>
/// <param name="propertiesStyle">
/// A <see cref="ReadStyle" /> value specifying at what level
/// of accuracy to read the media properties, or <see
/// cref="ReadStyle.None" /> to ignore the properties.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="abstraction" /> is <see langword="null"
/// />.
/// </exception>
public File (IFileAbstraction abstraction, ReadStyle propertiesStyle)
: base (abstraction, propertiesStyle)
{
}
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="File" /> for a specified file abstraction with an
/// average read style.
/// </summary>
/// <param name="abstraction">
/// A <see cref="TagLib.File.IFileAbstraction" /> object to use when
/// reading from and writing to the file.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="abstraction" /> is <see langword="null"
/// />.
/// </exception>
public File (IFileAbstraction abstraction)
: base (abstraction)
{
}
#endregion
#region Public Methods
/// <summary>
/// Gets a tag of a specified type from the current instance,
/// optionally creating a new tag if possible.
/// </summary>
/// <param name="type">
/// A <see cref="TagLib.TagTypes" /> value indicating the
/// type of tag to read.
/// </param>
/// <param name="create">
/// A <see cref="bool" /> value specifying whether or not to
/// try and create the tag if one is not found.
/// </param>
/// <returns>
/// A <see cref="Tag" /> object containing the tag that was
/// found in or added to the current instance. If no
/// matching tag was found and none was created, <see
/// langword="null" /> is returned.
/// </returns>
/// <remarks>
/// If a <see cref="TagLib.Id3v2.Tag" /> is added to the
/// current instance, it will be placed at the start of the
/// file. On the other hand, <see cref="TagLib.Id3v1.Tag" />
/// <see cref="TagLib.Ape.Tag" /> will be added to the end of
/// the file. All other tag types will be ignored.
/// </remarks>
public override Tag GetTag (TagTypes type, bool create)
{
Tag t = (Tag as TagLib.NonContainer.Tag).GetTag (type);
if (t != null || !create)
return t;
switch (type) {
case TagTypes.Id3v1:
return EndTag.AddTag (type, Tag);
case TagTypes.Id3v2:
return EndTag.AddTag (type, Tag);
case TagTypes.Ape:
return EndTag.AddTag (type, Tag);
default:
return null;
}
}
#endregion
#region Protected Methods
/// <summary>
/// Reads format specific information at the start of the
/// file.
/// </summary>
/// <param name="start">
/// A <see cref="long" /> value containing the seek position
/// at which the tags end and the media data begins.
/// </param>
/// <param name="propertiesStyle">
/// A <see cref="ReadStyle" /> value specifying at what level
/// of accuracy to read the media properties, or <see
/// cref="ReadStyle.None" /> to ignore the properties.
/// </param>
protected override void ReadStart (long start, ReadStyle propertiesStyle)
{
if ((propertiesStyle & ReadStyle.Average) == 0)
return;
FindMarker (ref start, Marker.SystemSyncPacket);
ReadSystemFile (start);
}
/// <summary>
/// Reads format specific information at the end of the
/// file.
/// </summary>
/// <param name="end">
/// A <see cref="long" /> value containing the seek position
/// at which the media data ends and the tags begin.
/// </param>
/// <param name="propertiesStyle">
/// A <see cref="ReadStyle" /> value specifying at what level
/// of accuracy to read the media properties, or <see
/// cref="ReadStyle.None" /> to ignore the properties.
/// </param>
protected override void ReadEnd (long end, ReadStyle propertiesStyle)
{
// Make sure we have ID3v1 and ID3v2 tags.
GetTag (TagTypes.Id3v1, true);
GetTag (TagTypes.Id3v2, true);
if ((propertiesStyle & ReadStyle.Average) == 0 || start_time == null)
return;
// Enable to search the marker in the entire file if none is found so far
if (end == Length)
end = 0;
RFindMarker (ref end, Marker.SystemSyncPacket);
end_time = ReadTimestamp (end + 4);
}
/// <summary>
/// Reads the audio properties from the file represented by
/// the current instance.
/// </summary>
/// <param name="start">
/// A <see cref="long" /> value containing the seek position
/// at which the tags end and the media data begins.
/// </param>
/// <param name="end">
/// A <see cref="long" /> value containing the seek position
/// at which the media data ends and the tags begin.
/// </param>
/// <param name="propertiesStyle">
/// A <see cref="ReadStyle" /> value specifying at what level
/// of accuracy to read the media properties, or <see
/// cref="ReadStyle.None" /> to ignore the properties.
/// </param>
/// <returns>
/// A <see cref="TagLib.Properties" /> object describing the
/// media properties of the file represented by the current
/// instance.
/// </returns>
protected override Properties ReadProperties (long start, long end, ReadStyle propertiesStyle)
{
TimeSpan duration = start_time == null ?
TimeSpan.Zero : TimeSpan.FromSeconds (end_time - (double)start_time);
return new Properties (duration, video_header, audio_header);
}
/// <summary>
/// Gets the marker at a specified position.
/// </summary>
/// <param name="position">
/// A <see cref="long" /> value specifying the postion in the
/// file represented by the current instance at which to
/// read.
/// </param>
/// <returns>
/// A <see cref="Marker" /> value containing the type of
/// marker found at the specified position.
/// </returns>
/// <exception cref="CorruptFileException">
/// A valid marker does not exist at the specified position.
/// </exception>
protected Marker GetMarker (long position)
{
Seek (position);
ByteVector identifier = ReadBlock (4);
if (identifier.Count == 4 && identifier.StartsWith (MarkerStart))
return (Marker)identifier[3];
throw new CorruptFileException ("Invalid marker at position " + position);
}
/// <summary>
/// Finds the next marker starting at a specified position.
/// </summary>
/// <param name="position">
/// A <see cref="long" /> value reference specifying the
/// position at which to start searching. This value
/// is updated to the position of the found marker.
/// </param>
/// <returns>
/// A <see cref="Marker" /> value containing the type of
/// marker found at the specified position.
/// </returns>
/// <exception cref="CorruptFileException">
/// A valid marker could not be found.
/// </exception>
protected Marker FindMarker (ref long position)
{
position = Find (MarkerStart, position);
if (position < 0)
throw new CorruptFileException ("Marker not found");
return GetMarker (position);
}
/// <summary>
/// Finds the next marker of a specified type, starting at a
/// specified position.
/// </summary>
/// <param name="position">
/// A <see cref="long" /> value reference specifying the
/// position at which to start searching. This value
/// is updated to the position of the found marker.
/// </param>
/// <param name="marker">
/// A <see cref="Marker" /> value specifying the type of
/// marker to search for.
/// </param>
/// <exception cref="CorruptFileException">
/// A valid marker could not be found.
/// </exception>
protected void FindMarker (ref long position, Marker marker)
{
ByteVector packet = new ByteVector (MarkerStart);
packet.Add ((byte)marker);
position = Find (packet, position);
if (position < 0)
throw new CorruptFileException ("Marker not found");
}
/// <summary>
/// Finds the previous marker of a specified type, starting
/// at a specified position.
/// </summary>
/// <param name="position">
/// A <see cref="long" /> value reference specifying the
/// position at which to start searching. This value
/// is updated to the position of the found marker.
/// </param>
/// <param name="marker">
/// A <see cref="Marker" /> value specifying the type of
/// marker to search for.
/// </param>
/// <exception cref="CorruptFileException">
/// A valid marker could not be found.
/// </exception>
protected void RFindMarker (ref long position, Marker marker)
{
ByteVector packet = new ByteVector (MarkerStart);
packet.Add ((byte)marker);
position = RFind (packet, position);
if (position < 0)
throw new CorruptFileException ("Marker not found");
}
/// <summary>
/// Reads the contents of the file as a system file, starting
/// at a specified position.
/// </summary>
/// <param name="position">
/// A <see cref="long" /> value specifying the postion in the
/// file represented by the current instance at which to
/// start reading.
/// </param>
/// <remarks>
/// This method will stop when it has read both an audio and
/// a video header, or once it's read 100 packets. This is to
/// prevent the entire file from being read if it lacks one
/// type of stream.
/// </remarks>
protected void ReadSystemFile (long position)
{
int sanity_limit = 100;
for (int i = 0; i < sanity_limit && (start_time == null ||
!audio_found || !video_found); i++) {
Marker marker = FindMarker (ref position);
switch (marker) {
case Marker.SystemSyncPacket:
ReadSystemSyncPacket (ref position);
break;
case Marker.SystemPacket:
case Marker.PaddingPacket:
Seek (position + 4);
position += ReadBlock (2).ToUShort () +
6;
break;
case Marker.VideoPacket:
ReadVideoPacket (ref position);
break;
case Marker.AudioPacket:
ReadAudioPacket (ref position);
break;
case Marker.EndOfStream:
return;
default:
position += 4;
break;
}
}
}
#endregion
#region Private Methods
/// <summary>
/// Reads an audio packet, assigning the audio header and
/// advancing the position to the next packet position.
/// </summary>
/// <param name="position">
/// A <see cref="long" /> value reference specifying the
/// position at which to start reading the packet. This value
/// is updated to the position of the next packet.
/// </param>
void ReadAudioPacket (ref long position)
{
Seek (position + 4);
int length = ReadBlock (2).ToUShort ();
if (!audio_found) {
// There is a maximum of 16 stuffing bytes, read up to the PTS/DTS flags
ByteVector packetHeaderBytes = this.ReadBlock (19);
int i = 0;
while (i < packetHeaderBytes.Count && packetHeaderBytes[i] == 0xFF) {
// Byte is a stuffing byte
i++;
}
if ((packetHeaderBytes[i] & 0x40) != 0) {
// STD buffer size is unexpected for audio packets, but whatever
i++;
}
// Decode the PTS/DTS flags
byte timestampFlags = packetHeaderBytes[i];
long dataOffset = 4 + 2 + i + // Packet marker + packet length + stuffing bytes/STD buffer size
((timestampFlags & 0x20) > 0 ? 4 : 0) + // Presentation timestamp
((timestampFlags & 0x10) > 0 ? 4 : 0); // Decode timestamp
audio_found = AudioHeader.Find (out audio_header, this, position + dataOffset, length - 9);
}
position += length;
}
/// <summary>
/// Reads a video packet, assigning the video header and
/// advancing the position to the next packet position.
/// </summary>
/// <param name="position">
/// A <see cref="long" /> value reference specifying the
/// position at which to start reading the packet. This value
/// is updated to the position of the next packet.
/// </param>
void ReadVideoPacket (ref long position)
{
Seek (position + 4);
int length = ReadBlock (2).ToUShort ();
long offset = position + 6;
while (!video_found && offset < position + length)
if (FindMarker (ref offset) == Marker.VideoSyncPacket) {
video_header = new VideoHeader (this, offset + 4);
video_found = true;
} else {
// advance the offset by 6 bytes, so the next iteration of the
// loop won't find the same marker and get stuck. 6 bytes because findMarker is a
// generic find that could get both PES packets and Stream packets, the smallest
// posible pes packet with a size =0 would be 6 bytes.
offset += 6;
}
position += length;
}
/// <summary>
/// Reads a system sync packet, filling in version
/// information and the first timestamp value, advancing the
/// position to the next packet position.
/// </summary>
/// <param name="position">
/// A <see cref="long" /> value reference specifying the
/// position at which to start reading the packet. If the
/// method is called without exception, this is updated to
/// the position of the next packet.
/// </param>
/// <exception cref="UnsupportedFormatException">
/// The MPEG version contained in the packet is unknown.
/// </exception>
void ReadSystemSyncPacket (ref long position)
{
int packet_size = 0;
Seek (position + 4);
byte version_info = ReadBlock (1)[0];
if ((version_info & 0xF0) == 0x20) {
version = Version.Version1;
packet_size = 12;
} else if ((version_info & 0xC0) == 0x40) {
version = Version.Version2;
Seek (position + 13);
packet_size = 14 + (ReadBlock (1)[0] & 0x07);
} else
throw new UnsupportedFormatException ("Unknown MPEG version.");
if (start_time == null)
start_time = ReadTimestamp (position + 4);
position += packet_size;
}
/// <summary>
/// Reads an MPEG timestamp from a specified position in the
/// file represented by the current instance.
/// </summary>
/// <param name="position">
/// A <see cref="long" /> value containing the position in
/// the file at which to read. This should be immediately
/// following a system sync packet marker.
/// </param>
/// <returns>
/// A <see cref="double" /> value containing the read time in
/// seconds.
/// </returns>
double ReadTimestamp (long position)
{
double high;
uint low;
Seek (position);
if (version == Version.Version1) {
ByteVector data = ReadBlock (5);
high = (data[0] >> 3) & 0x01;
low = ((uint)((data[0] >> 1) & 0x03) << 30) |
(uint)(data[1] << 22) |
(uint)((data[2] >> 1) << 15) |
(uint)(data[3] << 7) |
(uint)(data[4] >> 1);
} else {
ByteVector data = ReadBlock (6);
high = (data[0] & 0x20) >> 5;
low = ((uint)((data[0] & 0x18) >> 3) << 30) |
(uint)((data[0] & 0x03) << 28) |
(uint)(data[1] << 20) |
(uint)((data[2] & 0xF8) << 12) |
(uint)((data[2] & 0x03) << 13) |
(uint)(data[3] << 5) |
(uint)(data[4] >> 3);
}
return (((high * 0x10000) * 0x10000) + low) / 90000.0;
}
#endregion
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3e4313336bf17102cab942b5c644f815
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+178
View File
@@ -0,0 +1,178 @@
//
// VBRIHeader.cs: Provides information about a variable bitrate MPEG audio
// stream encoded with the Fraunhofer Encoder.
//
// Author:
// Helmut Wahrmann
//
// Original Source:
// XingHeader.cs
//
// Copyright (C) 2007 Helmut Wahrmann
// Copyright (C) 2005-2007 Brian Nickel
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License version
// 2.1 as published by the Free Software Foundation.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA
//
using System;
namespace TagLib.Mpeg
{
/// <summary>
/// This structure provides information about a variable bitrate MPEG
/// audio stream encoded by the Fraunhofer Encoder.
/// </summary>
public struct VBRIHeader
{
#region Public Fields
/// <summary>
/// Contains te VBRI identifier.
/// </summary>
/// <value>
/// "VBRI"
/// </value>
public static readonly ReadOnlyByteVector FileIdentifier = "VBRI";
/// <summary>
/// An empty and unset VBRI header.
/// </summary>
public static readonly VBRIHeader Unknown = new VBRIHeader (0, 0);
#endregion
#region Constructors
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="VBRIHeader" /> with a specified frame count and
/// size.
/// </summary>
/// <param name="frame">
/// A <see cref="uint" /> value specifying the frame count of
/// the audio represented by the new instance.
/// </param>
/// <param name="size">
/// A <see cref="uint" /> value specifying the stream size of
/// the audio represented by the new instance.
/// </param>
VBRIHeader (uint frame, uint size)
{
TotalFrames = frame;
TotalSize = size;
Present = false;
}
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="VBRIHeader" /> by reading its raw contents.
/// </summary>
/// <param name="data">
/// A <see cref="ByteVector" /> object containing the raw
/// VBRI header.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="data" /> is <see langword="null" />.
/// </exception>
/// <exception cref="CorruptFileException">
/// <paramref name="data" /> does not start with <see
/// cref="FileIdentifier" />.
/// </exception>
public VBRIHeader (ByteVector data)
{
if (data == null)
throw new ArgumentNullException (nameof (data));
// Check to see if a valid VBRI header is available.
if (!data.StartsWith (FileIdentifier))
throw new CorruptFileException ("Not a valid VBRI header");
// Size starts at Position 10
int position = 10;
TotalSize = data.Mid (position, 4).ToUInt ();
position += 4;
// The number of Frames are found at Posistion 14
TotalFrames = data.Mid (position, 4).ToUInt ();
position += 4;
Present = true;
}
#endregion
#region Public Properties
/// <summary>
/// Gets the total number of frames in the file, as indicated
/// by the current instance.
/// </summary>
/// <value>
/// A <see cref="uint" /> value containing the number of
/// frames in the file, or <c>0</c> if not specified.
/// </value>
public uint TotalFrames { get; private set; }
/// <summary>
/// Gets the total size of the file, as indicated by the
/// current instance.
/// </summary>
/// <value>
/// A <see cref="uint" /> value containing the total size of
/// the file, or <c>0</c> if not specified.
/// </value>
public uint TotalSize { get; private set; }
/// <summary>
/// Gets whether or not a physical VBRI header is present in
/// the file.
/// </summary>
/// <value>
/// A <see cref="bool" /> value indicating whether or not the
/// current instance represents a physical VBRI header.
/// </value>
public bool Present { get; private set; }
#endregion
#region Public Static Methods
/// <summary>
/// Gets the offset at which a VBRI header would appear in an
/// MPEG audio packet.
/// Always 32 bytes after the end of the first MPEG Header.
/// </summary>
/// <returns>
/// A <see cref="int" /> value indicating the offset in an
/// MPEG audio packet at which the VBRI header would appear.
/// </returns>
public static int VBRIHeaderOffset ()
{
// A VBRI header always appears 32 bytes after the end
// of the first MPEG Header. So it's position 36 (0x24).
return 0x24;
}
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 294f3abeffca1d37292a55c005b22d72
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+183
View File
@@ -0,0 +1,183 @@
//
// VideoHeader.cs: Provides information about an MPEG video stream.
//
// Author:
// Brian Nickel (brian.nickel@gmail.com)
//
// Copyright (C) 2007 Brian Nickel
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License version
// 2.1 as published by the Free Software Foundation.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA
//
using System;
namespace TagLib.Mpeg
{
/// <summary>
/// This structure implements <see cref="IVideoCodec" /> and provides
/// information about an MPEG video stream.
/// </summary>
public struct VideoHeader : IVideoCodec
{
#region Private Static Fields
/// <summary>
/// Contains frame rate values.
/// </summary>
static readonly double[] frame_rates = new double[9] {
0, 24000d/1001d, 24, 25, 30000d/1001d, 30, 50,
60000d/1001d, 60
};
#endregion
#region Private Fields
/// <summary>
/// Contains the index in <see cref="frame_rates" /> of the
/// video frame rate.
/// </summary>
readonly int frame_rate_index;
#endregion
#region Constructors
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="VideoHeader" /> by reading it from a specified
/// location in a specified file.
/// </summary>
/// <param name="file">
/// A <see cref="TagLib.File" /> object to read from.
/// </param>
/// <param name="position">
/// A <see cref="long" /> value indicating the position in
/// <paramref name="file" /> at which the header begins.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="file" /> is <see langword="null" />.
/// </exception>
/// <exception cref="CorruptFileException">
/// Insufficient data could be read for the header.
/// </exception>
public VideoHeader (TagLib.File file, long position)
{
if (file == null)
throw new ArgumentNullException (nameof (file));
file.Seek (position);
ByteVector data = file.ReadBlock (7);
if (data.Count < 7)
throw new CorruptFileException ("Insufficient data in header.");
VideoWidth = data.Mid (0, 2).ToUShort () >> 4;
VideoHeight = data.Mid (1, 2).ToUShort () & 0x0FFF;
frame_rate_index = data[3] & 0x0F;
VideoBitrate = (int)((data.Mid (4, 3).ToUInt () >> 6) & 0x3FFFF);
}
#endregion
#region Public Properties
/// <summary>
/// Gets the duration of the media represented by the current
/// instance.
/// </summary>
/// <value>
/// Always <see cref="TimeSpan.Zero" />.
/// </value>
public TimeSpan Duration {
get { return TimeSpan.Zero; }
}
/// <summary>
/// Gets the types of media represented by the current
/// instance.
/// </summary>
/// <value>
/// Always <see cref="MediaTypes.Video" />.
/// </value>
public MediaTypes MediaTypes {
get { return MediaTypes.Video; }
}
/// <summary>
/// Gets a text description of the media represented by the
/// current instance.
/// </summary>
/// <value>
/// A <see cref="string" /> object containing a description
/// of the media represented by the current instance.
/// </value>
public string Description {
get { return "MPEG Video"; }
}
/// <summary>
/// Gets the width of the video represented by the current
/// instance.
/// </summary>
/// <value>
/// A <see cref="int" /> value containing the width of the
/// video represented by the current instance.
/// </value>
public int VideoWidth { get; private set; }
/// <summary>
/// Gets the height of the video represented by the current
/// instance.
/// </summary>
/// <value>
/// A <see cref="int" /> value containing the height of the
/// video represented by the current instance.
/// </value>
public int VideoHeight { get; private set; }
/// <summary>
/// Gets the frame rate of the video represented by the
/// current instance.
/// </summary>
/// <value>
/// A <see cref="double" /> value containing the frame rate
/// of the video represented by the current instance.
/// </value>
public double VideoFrameRate {
get {
return frame_rate_index < 9 ? frame_rates[frame_rate_index] : 0;
}
}
/// <summary>
/// Gets the bitrate of the video represented by the current
/// instance.
/// </summary>
/// <value>
/// A <see cref="int" /> value containing a bitrate of the
/// video represented by the current instance.
/// </value>
public int VideoBitrate { get; private set; }
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c5c4123d394b96d2e92c98c84f0f7ba3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+192
View File
@@ -0,0 +1,192 @@
//
// XingHeader.cs: Provides information about a variable bitrate MPEG audio
// stream.
//
// Author:
// Brian Nickel (brian.nickel@gmail.com)
//
// Original Source:
// xingheader.cpp from TagLib
//
// Copyright (C) 2005-2007 Brian Nickel
// Copyright (C) 2003 by Ismael Orenstein (Original Implementation)
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License version
// 2.1 as published by the Free Software Foundation.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA
//
using System;
namespace TagLib.Mpeg
{
/// <summary>
/// This structure provides information about a variable bitrate MPEG
/// audio stream.
/// </summary>
public struct XingHeader
{
#region Public Fields
/// <summary>
/// Contains te Xing identifier.
/// </summary>
/// <value>
/// "Xing"
/// </value>
public static readonly ReadOnlyByteVector FileIdentifier = "Xing";
/// <summary>
/// An empty and unset Xing header.
/// </summary>
public static readonly XingHeader Unknown = new XingHeader (0, 0);
#endregion
#region Constructors
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="XingHeader" /> with a specified frame count and
/// size.
/// </summary>
/// <param name="frame">
/// A <see cref="uint" /> value specifying the frame count of
/// the audio represented by the new instance.
/// </param>
/// <param name="size">
/// A <see cref="uint" /> value specifying the stream size of
/// the audio represented by the new instance.
/// </param>
XingHeader (uint frame, uint size)
{
TotalFrames = frame;
TotalSize = size;
Present = false;
}
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="XingHeader" /> by reading its raw contents.
/// </summary>
/// <param name="data">
/// A <see cref="ByteVector" /> object containing the raw
/// Xing header.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="data" /> is <see langword="null" />.
/// </exception>
/// <exception cref="CorruptFileException">
/// <paramref name="data" /> does not start with <see
/// cref="FileIdentifier" />.
/// </exception>
public XingHeader (ByteVector data)
{
if (data == null)
throw new ArgumentNullException (nameof (data));
// Check to see if a valid Xing header is available.
if (!data.StartsWith (FileIdentifier))
throw new CorruptFileException ("Not a valid Xing header");
int position = 8;
if ((data[7] & 0x01) != 0) {
TotalFrames = data.Mid (position, 4).ToUInt ();
position += 4;
} else
TotalFrames = 0;
if ((data[7] & 0x02) != 0) {
TotalSize = data.Mid (position, 4).ToUInt ();
position += 4;
} else
TotalSize = 0;
Present = true;
}
#endregion
#region Public Properties
/// <summary>
/// Gets the total number of frames in the file, as indicated
/// by the current instance.
/// </summary>
/// <value>
/// A <see cref="uint" /> value containing the number of
/// frames in the file, or <c>0</c> if not specified.
/// </value>
public uint TotalFrames { get; private set; }
/// <summary>
/// Gets the total size of the file, as indicated by the
/// current instance.
/// </summary>
/// <value>
/// A <see cref="uint" /> value containing the total size of
/// the file, or <c>0</c> if not specified.
/// </value>
public uint TotalSize { get; private set; }
/// <summary>
/// Gets whether or not a physical Xing header is present in
/// the file.
/// </summary>
/// <value>
/// A <see cref="bool" /> value indicating whether or not the
/// current instance represents a physical Xing header.
/// </value>
public bool Present { get; private set; }
#endregion
#region Public Static Methods
/// <summary>
/// Gets the offset at which a Xing header would appear in an
/// MPEG audio packet based on the version and channel mode.
/// </summary>
/// <param name="version">
/// A <see cref="Version" /> value specifying the version of
/// the MPEG audio packet.
/// </param>
/// <param name="channelMode">
/// A <see cref="ChannelMode" /> value specifying the channel
/// mode of the MPEG audio packet.
/// </param>
/// <returns>
/// A <see cref="int" /> value indicating the offset in an
/// MPEG audio packet at which the Xing header would appear.
/// </returns>
public static int XingHeaderOffset (Version version, ChannelMode channelMode)
{
bool single_channel = channelMode == ChannelMode.SingleChannel;
if (version == Version.Version1)
return single_channel ? 0x15 : 0x24;
else
return single_channel ? 0x0D : 0x15;
}
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2581d23e885e083849a5fd14bc23a64c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: