Initial commit
This commit is contained in:
commit
262601edb8
2025 files changed
+218754
No files matched your search
@@ -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 & ~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:
|
||||
@@ -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:
|
||||
@@ -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 & ~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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e4313336bf17102cab942b5c644f815
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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:
|
||||
@@ -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:
|
||||
@@ -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:
|
||||
Reference in new issue
Block a user