Spieldauer MP3/MP4 Datei ermitteln

  • VB.NET

Es gibt 24 Antworten in diesem Thema. Der letzte Beitrag () ist von -Franky-.

    Spieldauer MP3/MP4 Datei ermitteln

    Hi,

    ich habe eine Anwendung, bei der ich die Spieldauer von MP3/MP4 Dateien ermitteln muss.

    Die Aufgabe hatte ich wie folgt gelöst:

    VB.NET-Quellcode

    1. Imports System.IO
    2. Public Class Form1
    3. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    4. Dim myFile As String = "... my Source ..."
    5. Dim myLength = GetMP3Length(myFile)
    6. Debug.Print("mylength=" & myLength.ToString)
    7. End Sub
    8. Private Function GetMP3Length(ByVal strFileName As String) As Long
    9. If Not File.Exists(strFileName) Then
    10. MessageBox.Show("GetMP3Length " & vbNewLine & strFileName & vbNewLine &
    11. " - file does not exists.")
    12. Return 0
    13. End If
    14. Dim strBuffer As New String(" "c, 255)
    15. Dim lRet As Long
    16. Dim sReturn As New String(" "c, 255)
    17. Dim myNullchar As Char = Convert.ToChar(0)
    18. ' Da die mciSendString Funktion mit langen Dateinamen
    19. ' nicht korrekt arbeitet, muss zuvor der kurze
    20. ' 8.3 Dateiname der MP3-Datei ermittelt werden.
    21. Try
    22. lRet = GetShortPathName(strFileName, strBuffer, strBuffer.Length)
    23. Catch ex As Exception
    24. MessageBox.Show("GetShortPathName failed " & vbNewLine &
    25. "Filename not changed" & vbNewLine &
    26. "File: " & strFileName & vbNewLine &
    27. ex.Message)
    28. End Try
    29. If lRet <> 0 Then
    30. strFileName = strBuffer.Substring(0, strBuffer.IndexOf(myNullchar))
    31. Else
    32. strFileName = strFileName
    33. End If
    34. Try
    35. ' MP3-Datei öffnen
    36. mciSendString("open " & """" & strFileName & """" &
    37. " type MPEGVideo alias mp3audio", "0", 0, 0)
    38. Catch ex As Exception
    39. MessageBox.Show("MP open failed " & vbNewLine &
    40. "Length=0 substituted" & vbNewLine &
    41. "File: " & strFileName & vbNewLine &
    42. ex.Message)
    43. Debug.Print(ex.Message)
    44. Return 0
    45. End Try
    46. ' Länge der Datei in Millisekunden auslesen
    47. lRet = mciSendString("status mp3audio length",
    48. sReturn, Len(sReturn), 0&)
    49. ' MP3-Datei schliessen
    50. mciSendString("close mp3audio", "0", 0, 0)
    51. GetMP3Length = CLng(Convert.ToDouble(sReturn.Trim))
    52. End Function
    53. Private Declare Function mciSendString Lib " winmm.dll" Alias " mciSendStringA" (ByVal lpstrCommand As String,
    54. ByVal lpstrReturnString As String,
    55. ByVal uReturnLength As Integer,
    56. ByVal hwndCallback As Integer) As Integer
    57. Private Declare Function GetShortPathName Lib " kernel32" Alias " GetShortPathNameA" (ByVal lpszLongPath As String,
    58. ByVal lpszShortPath As String,
    59. ByVal cchBuffer As Integer) As Integer
    60. End Class


    Diese Lösung funktioniert jetzt leider nicht mehr. Ich erhalte folgenden Laufzeitfehler beim Öffnen der Datei:

    Quellcode

    1. Unable to load DLL ' winmm.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)


    Irgend etwas hat sich mit der DLL geändert. Weiß jemand, wie ich das Dingens wieder zum Laufen bringe.

    LG
    Peter

    *Topic verschoben*

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Marcus Gräfe“ ()

    Kann es sein das sich bei Deinen APIs ein paar Leerzeichen bei Lib und Alias eingeschlichen haben? Auf die schnelle: hwndCallback kein Integer sondern ein IntPtr. Zu mciSendString: Kann sein das sich das mittlerweile geändert hat. Mir ist noch in Erinnerung das mciSendString Probleme mit MP3s hat, die mehr als 128kbit/s aufweisen und das die Spieldauer bei VBR-MP3s falsch zurück gegeben wird. Es gibt durchaus bessere Möglichkeiten als mciSendString zu verwenden um die Spieldauer zu ermitteln. Mir fallen da spontan gleich 6 Möglichkeiten ein.
    1. per Interface IPropertyStore
    2. per MediaFoundation
    3. per WinRT
    4. per ShellFolder/ShellItem.GetDetailsOf
    5. per WMP
    6. per Drittanbieter-Dlls wie zB. die bass.dll (zumindest für MP3).
    Punkt 1,2 und 3 wären meine bevorzugten Schnittstellen. ;)

    Edit: 7. Irgendwelche NuGet-Pakete die evtl. auch nur das bereits oben genannte nutzen. :D
    Mfg -Franky-

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „-Franky-“ ()

    @DTF Vllt hast ja Lust mal die MediaFoundation auszuprobieren. So vor Jahresende hab ich keine Lust mehr Code zu schreiben. :D

    1. API MFStartup(MF_VERSION_2, MFSTARTUP_FULL)
    2. API MFCreateSourceReaderFromURL(MediaFile, IntPtr.Zero, IMFSourceReader)
    3. IMFSourceReader.GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE, MF_PD_DURATION, PROPVARIANT)
    4. ist PROPVARIANT.vt = VARTYPE.VT_UI8 ? -> PROPVARIANT.uhVal -> Duration (-> TimeSpan)
    5. API PropVariantClear(PROPVARIANT)
    6. IMFSourceReader.Release
    7. API MFShutdown.

    Das Interface IMFSourceReader musst ja nicht komplett einbauen. Ein Pointer auf den IMFSourceReader reicht und dann rufst aus der VTable die Funktion GetPresentationAttribute = 12 auf. ;)
    Mfg -Franky-

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „-Franky-“ ()

    @-Franky-

    Ich habe bereits so einiges von der MF implementiert, dein kleiner Mediaplayer hatte mich dazu motiviert. Aber es fehlt mir die Zeit weiter zu machen. Ich habe ein Projekt angefangen womit ich die nächsten Jahre nebenbei zu tun haben werde. Hab kürzlich ein Spiel gekauft(Early Access), das mir gefällt aber total verbugt ist und noch so einiges negatives an den Tag bringt, deswegen hab ich den Spass dran verloren(hab Kontakt zu den Devs aufgenommen, aber ich traue denen nicht zu, das alles besser zu machen, die sind auch nur Hobbymässig dabei). So ein Spiel mach ich jetzt auch, habe die UnrealEngine jetzt ganz gut im Griff und starte voll durch.
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „DTF“ ()

    Wenn Peter329 sich mal versuchen will, ich glaube das Projekt hier war es, daran kann er sich orientieren, bzw. das erweitern. Kompliziert ist es wirklich nicht, da hast du absolut recht, sofern man das verstanden hat.
    Einfacher MediaPlayer per Media Foundation (IMFMediaEngine(Ex))
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D

    Peter329 schrieb:

    Unable to load DLL ' winmm.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)


    Langsam glaube ich Peter329 will uns ärgern. Woher kommen auf einmal die Leerzeichen die zu viel sind, vorher ging es ja sagt Peter329? Klar kann " winmm.dll"(beachtet das führende Leerzeichen!) nicht gefunden werden, bei GetShortPathName fehlt beim DLL-Namen auch die extension! @Peter329 wie kann das sein? Sogar bei den beiden Funktionsnamen ist ein führendes Leerzeichen. Ein Copy&Paste Fehler kann das eigendlich nicht sein, erklär mal wie sowas kommen kann.
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D
    Mit der MediaInfo.dll und der MediaInfo.Net.dll lassen sich derartige Informationen ganz easy auslesen.
    github.com/stax76/MediaInfo.NET
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Naja, nicht jeder mag sich die Arbeit machen und die Header oder IDLs zu suchen(wegen der Reihenfolge, was MS in der Doku ja nicht so hat, sondern Alphabetisch sortiert) und das dann in NET zu machen. Mann muss ja auch immer bei MS nachlesen, manche Funktionen sind unter Umständen nicht verfügbar, oder in manchen Zusammenhängen sind bestimmte Flags nicht unterstützt, oder nur wenn eine bestimmte andere Flag gesetzt ist. Ist immer viel zu lesen, sonst wundert man sich wenn auf einmal nicht klappt wie gedacht.

    Ehrlich gesagt, traue ich Peter das auch nicht zu mit den MF Interfaces zu machen. Da MS Mediaplayer empfiehlt auf der verlinkten Seite ist das Packet für ihm wohl die einfachste und somit beste Variante.
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D

    -Franky- schrieb:

    *Kopfschüttel* Sorry.
    Der Zweck heiligt die Mittel.
    Natürlich kann @Peter329 die Dateien einfach auf einen Meduiaplayer ziehen und feddich.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    DTF schrieb:

    Naja, nicht jeder mag sich die Arbeit machen und die Header oder IDLs zu suchen
    github.com/nihon-tc/Rtest/blob….0A/Include/mfreadwrite.h

    DTF schrieb:

    Mann muss ja auch immer bei MS nachlesen, manche Funktionen sind unter Umständen nicht verfügbar,
    Alle genannten MF-APIs sind Minimum ab WinVista verfügbar.

    DTF schrieb:

    Da MS Mediaplayer empfiehlt auf der verlinkten Seite ist das Packet für ihm wohl die einfachste und somit beste Variante.
    Da bringst was durcheinander. Die MediaInfo.dll ist nicht von Microsoft. Die stammt von hier: mediaarea.net/de/MediaInfo
    Mfg -Franky-
    MS empfiehlt die Mediaplayer Klasse womit MCI abgelöst wurde. Das Packet vereinfacht nur die Nutzung, wollte nicht sagen das das Packet von MS ist. Peter329 könnte zwar auch selbst die WinRT nutzen, aber ob er das zum laufen bekommt wäre die andere Frage. Bei einem FW Projekt evtl. ja, aber nicht bei einem NET Projekt, dazu haben wir leider keine Angabe von ihm.
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D

    DTF schrieb:

    Peter329 könnte zwar auch selbst die WinRT nutzen, ...
    Nun schauen wir erstmal, wenn Peter329 die zusätzlichen Leerzeichen bei den APIs entfernt hat, ob dann Peter329 sein Code läuft und ob es die von mir genannten Probleme noch gibt. Dann kann er immer noch darüber nachdenken auf eine andere, modernere Methode umzuschwenken. WinRT wäre da nur eine Möglichkeit. Die Duration per MediaFoundation oder per IPropertyStore auszulesen wären vom Aufwand her die einfachsten Möglichkeiten und das ganz ohne Dritt/Fremd-DLLs, extra Wrapper oder irgendwelche Verweise.

    Klar kann man auch mit einem Panzer zum einkaufen fahren. Mit dem Fahrrad oder zu Fuß geht's auch. Dauert evtl. etwas länger aber um eine Flasche Wasser zu kaufen reicht das allemal. ;)
    Mfg -Franky-

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „-Franky-“ ()

    -Franky- schrieb:

    Dauert evtl. etwas länger aber um eine Flasche Wasser zu kaufen reicht das allemal.


    Ja die hol ich mir Kistenweise am Kiosk, der ist beinahe gegenüber. Naja von Peter kommt irgendwie nichts mehr. Er hat scheinbar was er brauchte.

    Ich habe mal den Code von dem Aufnahmedatum den ich verlinkte mit einer MP3 probiert, für die Datei wird mir im Explorer eine Dauer angezeigt, aber via IPropertyStore bekomme ich die nicht. Das scheint also keine sichere Lösung zu sein, da könnte MF die bessere Variante sein.
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D

    DTF schrieb:

    aber via IPropertyStore bekomme ich die nicht
    Kleiner Tip: Es ist der CanonicalName "System.Media.Duration" -> API PSGetPropertyKeyFromName -> PROPERTYKEY ;)

    MF-Variante (weil heute Freitag ist, alles in eine Form geklatscht)
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Runtime.InteropServices
    2. Public Class Form1
    3. Private Const S_OK As Integer = 0
    4. Private Const MFSTARTUP_FULL As Integer = 0
    5. Private Const MF_VERSION_2 As Integer = &H20070
    6. Private Const MF_SOURCE_READER_MEDIASOURCE As Integer = &HFFFFFFFF
    7. Private Const MF_PD_DURATION As String = "6c990d33-bb8e-477a-8598-0d5d96fcd88a"
    8. Private Const IID_IMFSourceReader As String = "70ae66f2-c809-4e4f-8915-bdcb406b7993"
    9. Private Enum VARTYPE As UShort
    10. VT_UI8 = 21
    11. End Enum
    12. <StructLayout(LayoutKind.Explicit, Size:=16)>
    13. Private Structure PROPVARIANT
    14. <FieldOffset(0)> Dim vt As VARTYPE
    15. <FieldOffset(2)> Dim wReserved1 As UShort
    16. <FieldOffset(4)> Dim wReserved2 As UShort
    17. <FieldOffset(6)> Dim wReserved3 As UShort
    18. <FieldOffset(8)> Dim uhVal As ULong 'VT_UI8
    19. End Structure
    20. <DllImport("Ole32.dll", EntryPoint:="PropVariantClear")>
    21. <PreserveSig> Private Shared Function PropVariantClear(<[In], MarshalAs(UnmanagedType.Struct)> ByRef pVar As PROPVARIANT) As Integer
    22. End Function
    23. <DllImport("Mfplat.dll", EntryPoint:="MFStartup")>
    24. <PreserveSig> Private Shared Function MFStartup(<[In]> Version As Integer,
    25. <[In]> dwFlags As Integer) As Integer
    26. End Function
    27. <DllImport("Mfplat.dll", EntryPoint:="MFShutdown")>
    28. <PreserveSig> Private Shared Function MFShutdown() As Integer
    29. End Function
    30. <DllImport("Mfreadwrite.dll", EntryPoint:="MFCreateSourceReaderFromURL")>
    31. <PreserveSig> Private Shared Function MFCreateSourceReaderFromURL(<[In], MarshalAs(UnmanagedType.LPWStr)> pwszURL As String,
    32. <[In]> pAttributes As IntPtr,
    33. <Out, MarshalAs(UnmanagedType.Interface)> ByRef ppSourceReader As IMFSourceReader) As Integer
    34. End Function
    35. <ComImport>
    36. <InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
    37. <Guid(IID_IMFSourceReader)>
    38. Private Interface IMFSourceReader
    39. <PreserveSig> Function GetStreamSelection() As Integer
    40. <PreserveSig> Function SetStreamSelection() As Integer
    41. <PreserveSig> Function GetNativeMediaType() As Integer
    42. <PreserveSig> Function GetCurrentMediaType() As Integer
    43. <PreserveSig> Function SetCurrentMediaType() As Integer
    44. <PreserveSig> Function SetCurrentPosition() As Integer
    45. <PreserveSig> Function ReadSample() As Integer
    46. <PreserveSig> Function Flush() As Integer
    47. <PreserveSig> Function GetServiceForStream() As Integer
    48. <PreserveSig> Function GetPresentationAttribute(<[In]> dwStreamIndex As Integer,
    49. <[In]> ByRef guidAttribute As Guid,
    50. <Out, MarshalAs(UnmanagedType.Struct)> ByRef pvarAttribute As PROPVARIANT) As Integer
    51. End Interface
    52. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    53. Debug.Print(GetMediaDuration("d:\audio.mp3").ToString)
    54. End Sub
    55. Private Function GetMediaDuration(MediaFile As String) As TimeSpan
    56. Dim Ret As TimeSpan
    57. If MFStartup(MF_VERSION_2, MFSTARTUP_FULL) = S_OK Then
    58. Dim MFSourceReader As IMFSourceReader = Nothing
    59. If MFCreateSourceReaderFromURL(MediaFile, Nothing, MFSourceReader) = S_OK Then
    60. Dim PropVar As New PROPVARIANT
    61. If MFSourceReader.GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE,
    62. New Guid(MF_PD_DURATION),
    63. PropVar) = S_OK Then
    64. If PropVar.vt = VARTYPE.VT_UI8 Then
    65. Ret = New TimeSpan(Convert.ToInt64(PropVar.uhVal))
    66. End If
    67. PropVariantClear(PropVar)
    68. End If
    69. Marshal.ReleaseComObject(MFSourceReader)
    70. End If
    71. MFShutdown()
    72. End If
    73. Return Ret
    74. End Function
    75. End Class

    Mfg -Franky-
    Ich muss zurückrudern. Hatte einen Fehler im Pfad, nachdem ich dein Beispiel mit dem selben Pfad den ich einfach kopiert hab bekam ich 00:00:00 zu sehen, andere Datei probiert, Dauer wurde angezeigt.

    Ich habe den Verdacht das ich die Werte vom Ordner bekam
    Hatte den Pfad so: D:\\Musik\\Ordner\Dateiname.mp3 da fehlte ein backslash.

    Ist passiert als ich den Ordnernamen kopiert hab, hatte dann zwar zwei Backslahes getippt, aber der 2. muss beim einfügen des Dateinamens markiert gewesen sein. Ich gewöhne mir jetzt an bei Pfaden einen Backlash zu nutzen aber mit @, da passiert so ein Fehler wegen fehlendem escapen nicht noch mal. (Bin ja C# User, da sind default 2 backslashes zu verwenden)

    PS.

    Also via IPropertyStore geht es doch, mit PKEY_Media_Duration, PROPERTYKEY.fmtid = {64440490-4c8b-11d1-8b70-080036b11a03} und PROPERTYKEY.pid = 3
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „DTF“ ()