Die Community zu .NET und Classic VB.
Menü

VB.NET-Tipp 0146: Informationen zu Titel, Jahr, Interpret, Album und Genre aus MPEG 4-Audiodateien lesen

 von 

Beschreibung

Die vorgestellte Klasse ermöglicht es die Informationen zu Titel, Jahr, Interpret, Album und Genre aus einer MPEG 4-Audiodatei zu lesen. Eine Beschreibung des Aufbaus des MPEG4-Containers findet sich beispielsweise unter http://atomicparsley.sourceforge.net/mpeg-4files.html. Es ist möglich die Klasse zum Auslesen weiterer Metainformationen aus den Dateien zu erweitern.

Schwierigkeitsgrad:

Schwierigkeitsgrad 2

Framework-Version(en):

.NET Framework 1.0, .NET Framework 1.1, .NET Framework 2.0, .NET Framework 3.0, .NET Framework 3.5, .NET Framework 4

.NET-Version(en):

Visual Basic 2002, Visual Basic 2003, Visual Basic 2005, Visual Basic 2008, Visual Basic 2010

Download:

Download des Beispielprojektes [13,05 KB]

' Dieser Quellcode stammt von http://www.activevb.de
' und kann frei verwendet werden. Für eventuelle Schäden
' wird nicht gehaftet.

' Um Fehler oder Fragen zu klären, nutzen Sie bitte unser Forum.
' Ansonsten viel Spaß und Erfolg mit diesem Source!

' Projektversion:   Visual Studio 2008
' Option Strict:    Aus
' Option Explicit:  An
' Option Infer:     An
'
' Referenzen: 
'  - System
'  - System.Data
'  - System.Deployment
'  - System.Drawing
'  - System.Windows.Forms
'  - System.Xml
'  - System.Core
'  - System.Xml.Linq
'  - System.Data.DataSetExtensions
'
' Imports: 
'  - Microsoft.VisualBasic
'  - System
'  - System.Collections
'  - System.Collections.Generic
'  - System.Data
'  - System.Drawing
'  - System.Diagnostics
'  - System.Windows.Forms
'  - System.Linq
'  - System.Xml.Linq
'

' ##############################################################################
' ################################# Form1.vb ###################################
' ##############################################################################
Public Class Form1
    Private Sub Button1_Click(ByVal sender As System.Object, _
                              ByVal e As System.EventArgs) Handles Button1.Click
        Dim ofd As New OpenFileDialog _
            With {.Filter = "MPEG4 Files|*.mp4;*.m4a"}

        ofd.ShowDialog()

        If IO.File.Exists(ofd.FileName) Then
            Dim Info As New MPEG4Info(ofd.FileName)
            lstInfo.Items.Add("Info geladen aus " & ofd.FileName)
            lstInfo.Items.Add("Titel: " & Info.Title)
            lstInfo.Items.Add("Interpret: " & Info.Artist)
            lstInfo.Items.Add("Album: " & Info.Album)
            lstInfo.Items.Add("Jahr: " & Info.Year)
            lstInfo.Items.Add("Genre: " & Info.Genre)
        End If
    End Sub
End Class

' ##############################################################################
' ############################### MPEG4Class.vb ################################
' ##############################################################################
Class MPEG4Info
    Private FileName As String = ""     ' Pfad zur Datei
    Private ILST() As Byte              ' Byte Array für die Box ILST
    Private cDay() As Byte              ' Byte Array für die Box ©day
    Private cArt() As Byte              ' Byte Array für die Box ©art
    Private cAlb() As Byte              ' Byte Array für die Box ©alb
    Private cNam() As Byte              ' Byte Array für die Box ©nam
    Private cGen() As Byte              ' Byte Array für die Box ©gen/gnre

    Sub New(ByVal File As String)
        FileName = File

        ' Auslesen der Box ILST
        ILST = ReadBox("ilst")
    End Sub

    ' Hier wird eine Box aus der Datei in ein Byte-Array eingelesen 
    Private Function ReadBox(ByVal BoxName As String) As Byte()
        Dim ReturnBytes() As Byte
        Dim FileStram As New IO.FileStream(FileName, IO.FileMode.Open)
        Dim TempArr(3) As Byte
        Dim Pos As Long
        Dim TempString As String = ""
        Dim Len As Long

        ' Hier wird jede 4er-Bytekette im Filestream nach dem Boxnamen 
        '  durchsucht.
        For i = 3 To FileStram.Length - 1
            FileStram.Position = i - 3
            FileStram.Read(TempArr, 0, 4)

            TempString = ""

            For j = 0 To TempArr.Count - 1
                TempString &= Chr(TempArr(j))
            Next

            ' Wenn der Boxname gefunden wurde, wird der Anfangspunkt der Kette 
            '  im Array an die Startpositions-Variable übergeben.
            If TempString.ToUpper = BoxName.ToUpper Then
                Pos = i - 3
                Exit For
            ElseIf i = FileStram.Length - 1 Then
                ' Wird die Box nicht gefundet, gebe Nothing zurück
                Return Nothing
                Exit Function
            End If
        Next

        ' Hier werden die Größenbytes der Box ausgelesen ...
        FileStram.Position = Pos - 4
        FileStram.Read(TempArr, 0, 4)

        ' ... ausgerechnet und an die Längenvariable (Länge in Bytes) übergeben. 
        Array.Reverse(TempArr)
        Len = BitConverter.ToInt32(TempArr, 0)

        ' Jetzt wird nur noch die Leseposition an die Stelle gesetzt, an der der 
        '  Inhalt der Box beginnt und die Rückgabebytes auf die richtige Größe 
        '  gebracht.
        FileStram.Position = Pos + 4
        ReDim ReturnBytes(Len - 9)
        ' Hier wird der Inhalt der Box ausgelesen (Len-8, da die Größe 8 Bytes 
        '  für größenangabe und Boxname enthält)
        FileStram.Read(ReturnBytes, 0, Len - 8)

        Return ReturnBytes
    End Function

    ' Hier wird eine Box aus einem Byte-Array in ein Byte-Array eingelesen
    '  (ähnlich ReadBox)
    Private Function ReadChildBox(ByVal ChildBoxName As String, _
                                  ByVal ParentBoxData() As Byte) As Byte()
        Dim ReturnBytes() As Byte
        Dim Pos As Long
        Dim temparr(3) As Byte
        Dim TempString As String = ""
        Dim Len As Long

        If ParentBoxData Is Nothing Then
            Return Nothing
        End If

        For i = 3 To ParentBoxData.Length - 1
            TempString = ""

            For j = 3 To 0 Step -1
                TempString &= Chr(ParentBoxData(i - j))
            Next

            If TempString.ToUpper = ChildBoxName.ToUpper Then
                Pos = i - 3
                Exit For
            ElseIf i = ParentBoxData.Length - 1 Then
                Return Nothing
                Exit Function
            End If
        Next

        For i = Pos - 4 To Pos - 1
            temparr(i - (Pos - 4)) = ParentBoxData(i)
        Next

        Array.Reverse(temparr)
        Len = BitConverter.ToInt32(temparr, 0)

        ReDim ReturnBytes(Len - 9)
        For i = Pos + 4 To Pos - 5 + Len
            ReturnBytes(i - (Pos + 4)) = ParentBoxData(i)
        Next

        Return ReturnBytes
    End Function

    'Mit dieser Funktion wird der zur GenreID gehorende String ermittelt
    Private Function GetGenreString(ByVal GenreID As Integer) As String
        Dim AvailableGenres() As String = {"Blues", "Classic Rock", "Country", _
            "Dance", "Disco", "Funk", "Grunge", "Hip - Hop", "Jazz", "Metal", _
            "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", _
            "Rock", "Techno", "Industrial", "Alternative", "Ska", _
            "Death Metal", "Pranks", "Soundtrack", "Euro -Techno", "Ambient", _
            "Trip -Hop", "Vocal", "Jazz Funk", "Fusion", "Trance", _
            "Classical", "Instrumental", "Acid", "House", "Game", _
            "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass", "Soul", _
            "Punk", "Space", "Meditative", "Instrumental Pop", _
            "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", _
            "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", _
            "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", "Top 40", _
            "Christian Rap", "Pop/Funk", "Jungle", "Native American", _
            "Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes", _
            "Trailer", "Lo - Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", _
            "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk", _
            "Folk/Rock", "National Folk", "Swing", "Bebob", "Latin", _
            "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", _
            "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", _
            "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic", _
            "Humour", "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", _
            "Symphony", "Booty Bass", "Primus", "Porn Groove", "Satire", _
            "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad", _
            "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet", "Punk Rock", _
            "Drum Solo", "A Cappella", "Euro - House", "Dance Hall", "Goa", _
            "Drum & Bass", "Club - House", "Hardcore", "Terror", "Indie", _
            "BritPop", "Negerpunk", "Polsk Punk", "Beat", _
            "Christian Gangsta Rap", "Heavy Metal", "Black Metal", _
            "Crossover", "Contemporary Christian", "Christian Rock", _
            "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop", "Synthpop"}
        Try
            Return AvailableGenres(GenreID)
        Catch
            Return " "
        End Try
    End Function

    ReadOnly Property Year As String
        Get
            Dim RetString As String = ""

            cDay = ReadChildBox("©day", ILST)
            cDay = ReadChildBox("data", cDay)

            If cDay Is Nothing Then Return ""

            For i = 8 To cDay.Length - 1
                If InStr("0123456789", Chr(cDay(i))) > 0 Then
                    RetString &= Chr(cDay(i))
                End If
            Next

            Return RetString
        End Get
    End Property

    ReadOnly Property Title As String
        Get
            Dim RetString As String = ""

            cNam = ReadChildBox("©nam", ILST)
            cNam = ReadChildBox("data", cNam)

            If cNam Is Nothing Then Return ""

            For i = 8 To cNam.Length - 1
                RetString &= Chr(cNam(i))
            Next

            Return RetString
        End Get
    End Property

    ReadOnly Property Album As String
        Get
            Dim RetString As String = ""

            cAlb = ReadChildBox("©alb", ILST)
            cAlb = ReadChildBox("data", cAlb)

            If cAlb Is Nothing Then Return ""

            For i = 8 To cAlb.Length - 1
                RetString &= Chr(cAlb(i))
            Next

            Return RetString
        End Get
    End Property

    ReadOnly Property Artist As String
        Get
            Dim RetString As String = ""

            cArt = ReadChildBox("©art", ILST)
            cArt = ReadChildBox("data", cArt)

            If cArt Is Nothing Then Return ""

            For i = 8 To cArt.Length - 1
                RetString &= Chr(cArt(i))
            Next

            Return RetString
        End Get
    End Property

    ReadOnly Property Genre As String
        Get
            Dim RetString As String = ""

            cGen = ReadChildBox("gnre", ILST)
            cGen = ReadChildBox("data", cGen)

            If cGen Is Nothing Then
                cGen = ReadChildBox("©gen", ILST)
                cGen = ReadChildBox("data", cGen)

                If cGen Is Nothing Then Return ""

                For i = 8 To cGen.Length - 1
                    RetString &= Chr(cGen(i))
                Next

                Return RetString
            End If

            RetString = GetGenreString(cGen(cGen.Length - 1))

            Return RetString
        End Get
    End Property
End Class

' ##############################################################################
' ######################## WindowsApplication2.vbproj ##########################
' ##############################################################################
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
    <ProductVersion>9.0.30729</ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{F513AE9A-64B2-4F7B-943B-6E5F77CF496E}</ProjectGuid>
    <OutputType>WinExe</OutputType>
    <StartupObject>WindowsApplication1.My.MyApplication</StartupObject>
    <RootNamespace>WindowsApplication1</RootNamespace>
    <AssemblyName>WindowsApplication2</AssemblyName>
    <FileAlignment>512</FileAlignment>
    <MyType>WindowsForms</MyType>
    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
    <PlatformTarget>x86</PlatformTarget>
    <DebugSymbols>True</DebugSymbols>
    <DebugType>full</DebugType>
    <DefineDebug>True</DefineDebug>
    <DefineTrace>True</DefineTrace>
    <OutputPath>bin\Debug\</OutputPath>
    <DocumentationFile>WindowsApplication2.xml</DocumentationFile>
    <NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
    <PlatformTarget>x86</PlatformTarget>
    <DebugType>pdbonly</DebugType>
    <DefineDebug>False</DefineDebug>
    <DefineTrace>True</DefineTrace>
    <Optimize>True</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DocumentationFile>WindowsApplication2.xml</DocumentationFile>
    <NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
  </PropertyGroup>
  <PropertyGroup>
    <OptionExplicit>On</OptionExplicit>
  </PropertyGroup>
  <PropertyGroup>
    <OptionCompare>Binary</OptionCompare>
  </PropertyGroup>
  <PropertyGroup>
    <OptionStrict>Off</OptionStrict>
  </PropertyGroup>
  <PropertyGroup>
    <OptionInfer>On</OptionInfer>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Data" />
    <Reference Include="System.Deployment" />
    <Reference Include="System.Drawing" />
    <Reference Include="System.Windows.Forms" />
    <Reference Include="System.Xml" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
  </ItemGroup>
  <ItemGroup>
    <Import Include="Microsoft.VisualBasic" />
    <Import Include="System" />
    <Import Include="System.Collections" />
    <Import Include="System.Collections.Generic" />
    <Import Include="System.Data" />
    <Import Include="System.Drawing" />
    <Import Include="System.Diagnostics" />
    <Import Include="System.Windows.Forms" />
    <Import Include="System.Linq" />
    <Import Include="System.Xml.Linq" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Form1.vb">
      <SubType>Form</SubType>
    </Compile>
    <Compile Include="Form1.Designer.vb">
      <DependentUpon>Form1.vb</DependentUpon>
      <SubType>Form</SubType>
    </Compile>
    <Compile Include="MPEG4Class.vb" />
    <Compile Include="My Project\AssemblyInfo.vb" />
    <Compile Include="My Project\Application.Designer.vb">
      <AutoGen>True</AutoGen>
      <DependentUpon>Application.myapp</DependentUpon>
    </Compile>
    <Compile Include="My Project\Resources.Designer.vb">
      <AutoGen>True</AutoGen>
      <DesignTime>True</DesignTime>
      <DependentUpon>Resources.resx</DependentUpon>
    </Compile>
    <Compile Include="My Project\Settings.Designer.vb">
      <AutoGen>True</AutoGen>
      <DependentUpon>Settings.settings</DependentUpon>
      <DesignTimeSharedInput>True</DesignTimeSharedInput>
    </Compile>
  </ItemGroup>
  <ItemGroup>
    <EmbeddedResource Include="Form1.resx">
      <DependentUpon>Form1.vb</DependentUpon>
    </EmbeddedResource>
    <EmbeddedResource Include="My Project\Resources.resx">
      <Generator>VbMyResourcesResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.vb</LastGenOutput>
      <CustomToolNamespace>My.Resources</CustomToolNamespace>
      <SubType>Designer</SubType>
    </EmbeddedResource>
  </ItemGroup>
  <ItemGroup>
    <None Include="My Project\Application.myapp">
      <Generator>MyApplicationCodeGenerator</Generator>
      <LastGenOutput>Application.Designer.vb</LastGenOutput>
    </None>
    <None Include="My Project\Settings.settings">
      <Generator>SettingsSingleFileGenerator</Generator>
      <CustomToolNamespace>My</CustomToolNamespace>
      <LastGenOutput>Settings.Designer.vb</LastGenOutput>
    </None>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
  <!-- To modify your build process, add your task inside one Of the targets below And uncomment it. 
       Other similar extension points exist, see Microsoft.Common.targets.
  <Target Name="BeforeBuild">
  </Target>
  <Target Name="AfterBuild">
  </Target>
  -->
</Project>

Ihre Meinung  

Falls Sie Fragen zu diesem Artikel haben oder Ihre Erfahrung mit anderen Nutzern austauschen möchten, dann teilen Sie uns diese bitte in einem der unten vorhandenen Themen oder über einen neuen Beitrag mit. Hierzu können sie einfach einen Beitrag in einem zum Thema passenden Forum anlegen, welcher automatisch mit dieser Seite verknüpft wird.