Mit ID3v2 das Artwork eines Liedes auslesen

  • C#

Es gibt 8 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    Mit ID3v2 das Artwork eines Liedes auslesen

    Hey Leute,

    ich arbeite aktuell an einer Lib, um die Tags einer Mp3 Datei auszulesen. Ich kann auch alle Tags auslesen außer dem Cover. Ich habe als erstes mich selbst daran versucht, danach habe ich den Code von Perry Butler (Link) benutzt. Allerdings kommt bei ihm seinem Code der selbe Fehler. Davon abgesehen scheint ihm das auch bewusst gewesen zu sein, denn er hat diese Function nirgends genutzt. Jetzt stehe ich wieder bei dem Problem Anfang und weiß nicht weiter. Der Fehler tritt an folgeder Stelle auf:

    C#-Quellcode

    1. MemoryStream ms;
    2. ms = new MemoryStream(argFrame.ValueBytes, mApicDataStart, argFrame.ValueBytes.Length - mApicDataStart);
    3. img = Image.FromStream(ms);


    Er spuckt dabei die Fehlermeldung: "System.ArgumentException: 'Ungültiger Parameter.'" aus. An dem argFrame.ValueBytes kann es nicht liegen, der gibt immer den byte Array des Bildes aus.

    Ich habe die Bytes für das Bild mit dem Tag-Mapping-Code "APIC" ausgelesen.

    Und ich hatte gelesen, dass der Fehler auftreten kann, wenn das Bild ein png oder größer als eine gewisse Pixelanzahl ist. Also habe ich das Cover bei einem Lied mit einem Jpeg mit 50x50 pixel ersetzt.
    if Brain.Enabled = False Then
    Process.start("C:\Brain.exe")
    End if
    __________________________________________________

    Error: Brain.exe not found System shut down
    @Toni03 Kannst Du mal so viel Daten und Code posten, dass wir den Effekt ohne nochmal nachfragen zu müssen reproduzieren können?
    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!
    Habe jetzt alles zusammengetragen:

    Spoiler anzeigen

    C#-Quellcode

    1. ​using Microsoft.VisualBasic.CompilerServices;
    2. using System;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using System.ComponentModel;
    6. using System.Data;
    7. using System.Drawing;
    8. using System.IO;
    9. using System.Linq;
    10. using System.Text;
    11. using System.Threading.Tasks;
    12. using System.Windows.Forms;
    13. namespace Mp3LibTest
    14. {
    15. public partial class Form1 : Form
    16. {
    17. public Form1()
    18. {
    19. InitializeComponent();
    20. }
    21. private void button1_Click(object sender, EventArgs e)
    22. {
    23. FileDialog fileDialog = new OpenFileDialog();
    24. if(fileDialog.ShowDialog() == DialogResult.OK)
    25. {
    26. ID3v2Tag(new FileInfo(fileDialog.FileName));
    27. pictureBox1.Image = Cover();
    28. }
    29. }
    30. public ArrayList Frames = new ArrayList();
    31. public string TagSize;
    32. public System.IO.FileStream gTagReader;
    33. public string TagVersion;
    34. public string HeaderFlags;
    35. public string FileIdentifier;
    36. public class FrameBase
    37. {
    38. public string ID;
    39. public int Size;
    40. public string Flags;
    41. public byte[] ValueBytes;
    42. public string Value()
    43. {
    44. return ConvertBytesToText(ValueBytes);
    45. }
    46. }
    47. Image GetFrameImage(string FrameName)
    48. {
    49. for (int i = 0; i <= this.Frames.Count; i++)
    50. {
    51. FrameBase mFrame;
    52. if (Frames.Count > i)
    53. mFrame = (FrameBase)Frames[i];
    54. else break;
    55. if (mFrame.ID == FrameName) return GetCover(mFrame);
    56. }
    57. return null;
    58. }
    59. private Image GetCover(FrameBase argFrame)
    60. {
    61. Image img = null;
    62. byte mApicTextEncoding;
    63. ArrayList mApicMimeType = new ArrayList();
    64. byte mApicPictureType;
    65. string mApicDescription = "";
    66. int mApicDataStart = 7;
    67. string c = "";
    68. int i = 0;
    69. mApicTextEncoding = argFrame.ValueBytes[i];
    70. i++;
    71. c = "";
    72. while (c != "0")
    73. {
    74. c = argFrame.ValueBytes[i].ToString();
    75. mApicMimeType.Add(c);
    76. i++;
    77. }
    78. mApicPictureType = argFrame.ValueBytes[i];
    79. i++;
    80. c = "";
    81. while (c != "0")
    82. {
    83. c = argFrame.ValueBytes[i].ToString();
    84. mApicDescription += c.ToString();
    85. i++;
    86. }
    87. MemoryStream ms;
    88. ms = new MemoryStream(argFrame.ValueBytes, mApicDataStart, argFrame.ValueBytes.Length - mApicDataStart);
    89. img = Image.FromStream(ms);
    90. return img;
    91. }
    92. public void ID3v2Tag(System.IO.FileInfo fileInfo)
    93. {
    94. ParseTag(fileInfo);
    95. }
    96. public static string ConvertBytesToText(byte[] argBytes)
    97. {
    98. ArrayList arrayList = new ArrayList((ICollection)argBytes);
    99. return Encoding.ASCII.GetString(Array.FindAll<byte>(argBytes, new Predicate<byte>(isAlphaNumeric)));
    100. }
    101. public static bool isAlphaNumeric(byte b)
    102. {
    103. if (b > 31 & b < 128)
    104. return true;
    105. else
    106. return false;
    107. }
    108. public Image Cover()
    109. {
    110. return GetFrameImage("APIC");
    111. }
    112. void ParseTag(System.IO.FileInfo fileInfo)
    113. {
    114. gTagReader = fileInfo.OpenRead();
    115. ParseTagVersion();
    116. switch (this.TagVersion)
    117. {
    118. case "2.0":
    119. ParseHeaderV2();
    120. ParseFramesV2();
    121. break;
    122. case "3.0":
    123. ParseHeaderV4();
    124. ParseFramesV3();
    125. break;
    126. case "4.0":
    127. ParseHeaderV4();
    128. ParseFramesV4();
    129. break;
    130. case "Unknown Descriptor":
    131. break;
    132. }
    133. gTagReader.Close();
    134. gTagReader.Dispose();
    135. }
    136. void ParseTagVersion()
    137. {
    138. byte[] mTagDescriptor = new byte[3];
    139. byte[] mTagVersionBytes = new byte[2];
    140. this.gTagReader.Read(mTagDescriptor, 0, 3);
    141. this.gTagReader.Read(mTagVersionBytes, 0, 2);
    142. if (ConvertBytesToText(mTagDescriptor) == "ID3")
    143. {
    144. this.FileIdentifier = "ID3";
    145. this.TagVersion = (mTagVersionBytes[0]) + "." + (mTagVersionBytes[1]);
    146. }
    147. else
    148. this.TagVersion = "";
    149. }
    150. void ParseFramesV2()
    151. {
    152. gTagReader.Position = 10;
    153. while (!(gTagReader.Position >= Convert.ToInt32(this.TagSize)))
    154. {
    155. FrameBase mFrame = new FrameBase();
    156. byte[] mFrameIdBytes = new byte[3];
    157. byte[] mFrameSizeBytes = new byte[3];
    158. gTagReader.Read(mFrameIdBytes, 0, 3);
    159. gTagReader.Read(mFrameSizeBytes, 0, 3);
    160. if (mFrameIdBytes[0] == 0) { break; }
    161. string mFrameID = ConvertBytesToText(mFrameIdBytes);
    162. mFrame.ID = mFrameID;
    163. int mSizeV2 = GetID3EncodedSizeV2(mFrameSizeBytes);
    164. mFrame.Size = mSizeV2;
    165. Array.Resize(ref mFrame.ValueBytes, mFrame.Size);
    166. gTagReader.Read(mFrame.ValueBytes, 0, mFrame.Size);
    167. string mVal = mFrame.Value();
    168. this.Frames.Add(mFrame);
    169. }
    170. }
    171. void ParseFramesV3()
    172. {
    173. gTagReader.Position = 10;
    174. while (!(gTagReader.Position >= Convert.ToInt32(this.TagSize)))
    175. {
    176. FrameBase mFrame = new FrameBase();
    177. byte[] mFrameIdBytes = new byte[4];
    178. byte[] mFrameSizeBytes = new byte[4];
    179. byte[] mFrameFlagsBytes = new byte[4];
    180. gTagReader.Read(mFrameIdBytes, 0, 4);
    181. gTagReader.Read(mFrameSizeBytes, 0, 4);
    182. gTagReader.Read(mFrameFlagsBytes, 0, 2);
    183. if (mFrameIdBytes[0] == 0) { break; }
    184. string mFrameID = ConvertBytesToText(mFrameIdBytes);
    185. mFrame.ID = mFrameID;
    186. int mSizeV3 = GetID3EncodedSizeV2(mFrameSizeBytes);
    187. mFrame.Size = mSizeV3;
    188. string flags = GetBitString(mFrameFlagsBytes);
    189. if (ExamineBit(mFrameFlagsBytes[1], 8) == true) mFrame.Flags += "Tag Alter Preservation, ";
    190. if (ExamineBit(mFrameFlagsBytes[1], 7) == true) mFrame.Flags += "File Alter Preservation, ";
    191. if (ExamineBit(mFrameFlagsBytes[1], 6) == true) mFrame.Flags += "Read Only, ";
    192. if (ExamineBit(mFrameFlagsBytes[0], 8) == true) mFrame.Flags += "Compression, ";
    193. if (ExamineBit(mFrameFlagsBytes[0], 7) == true) mFrame.Flags += "Encryption, ";
    194. if (ExamineBit(mFrameFlagsBytes[0], 6) == true) mFrame.Flags += "Group Identity";
    195. Array.Resize(ref mFrame.ValueBytes, mFrame.Size);
    196. gTagReader.Read(mFrame.ValueBytes, 0, mFrame.Size);
    197. string mVal = mFrame.Value();
    198. this.Frames.Add(mFrame);
    199. }
    200. }
    201. void ParseFramesV4()
    202. {
    203. gTagReader.Position = 10;
    204. while (!(gTagReader.Position >= Convert.ToInt32(this.TagSize)))
    205. {
    206. FrameBase mFrame = new FrameBase();
    207. byte[] mFrameIdBytes = new byte[4];
    208. byte[] mFrameSizeBytes = new byte[4];
    209. byte[] mFrameFlagsBytes = new byte[2];
    210. gTagReader.Read(mFrameIdBytes, 0, 4);
    211. gTagReader.Read(mFrameSizeBytes, 0, 4);
    212. gTagReader.Read(mFrameFlagsBytes, 0, 2);
    213. if (mFrameIdBytes[0] == 0) { break; }
    214. string mFrameID = ConvertBytesToText(mFrameIdBytes);
    215. mFrame.ID = mFrameID;
    216. int mSizeV2 = GetID3EncodedSizeV2(mFrameSizeBytes);
    217. mFrame.Size = mSizeV2;
    218. int mSizeV3 = GetID3EncodedSizeV2(mFrameSizeBytes);
    219. mFrame.Size = mSizeV3;
    220. int mSizeV4 = GetID3EncodedSizeV2(mFrameSizeBytes);
    221. mFrame.Size = mSizeV4;
    222. string flags = GetBitString(mFrameFlagsBytes);
    223. if (ExamineBit(mFrameFlagsBytes[1], 7) == true) mFrame.Flags += "Tag Alter Preservation, ";
    224. if (ExamineBit(mFrameFlagsBytes[1], 6) == true) mFrame.Flags += "File Alter Preservation, ";
    225. if (ExamineBit(mFrameFlagsBytes[1], 5) == true) mFrame.Flags += "Read Only, ";
    226. if (ExamineBit(mFrameFlagsBytes[0], 7) == true) mFrame.Flags += "Group Identity, ";
    227. if (ExamineBit(mFrameFlagsBytes[0], 4) == true) mFrame.Flags += "Compression, ";
    228. if (ExamineBit(mFrameFlagsBytes[0], 3) == true) mFrame.Flags += "Encryption";
    229. if (ExamineBit(mFrameFlagsBytes[0], 2) == true) mFrame.Flags += "Unsynchronization";
    230. if (ExamineBit(mFrameFlagsBytes[0], 1) == true) mFrame.Flags += "Data Length Indicator";
    231. Array.Resize(ref mFrame.ValueBytes, mFrame.Size);
    232. gTagReader.Read(mFrame.ValueBytes, 0, mFrame.Size);
    233. string mVal = mFrame.Value();
    234. this.Frames.Add(mFrame);
    235. }
    236. }
    237. public static string GetBitString(byte[] argBytes)
    238. {
    239. string s = "";
    240. for (int i = 0; i <= argBytes.Length - 1; i++)
    241. {
    242. s += ExamineBit(argBytes[i], 0);
    243. s += ExamineBit(argBytes[i], 1);
    244. s += ExamineBit(argBytes[i], 2);
    245. s += ExamineBit(argBytes[i], 3);
    246. s += ExamineBit(argBytes[i], 4);
    247. s += ExamineBit(argBytes[i], 5);
    248. s += ExamineBit(argBytes[i], 6);
    249. s += ExamineBit(argBytes[i], 7);
    250. s += ExamineBit(argBytes[i], 8);
    251. }
    252. return s;
    253. }
    254. void ParseHeaderV2()
    255. {
    256. byte[] mHeaderFlags = new byte[2];
    257. byte[] mTagSize = new byte[4];
    258. gTagReader.Read(mHeaderFlags, 0, 1);
    259. if (ExamineBit(mHeaderFlags[0], 8) == true) this.HeaderFlags = "Unsynchronization";
    260. if (ExamineBit(mHeaderFlags[0], 7) == true) this.HeaderFlags += ", " + "Extended Header";
    261. if (ExamineBit(mHeaderFlags[0], 6) == true) this.HeaderFlags += ", " + "Experimental Indicator";
    262. gTagReader.Read(mTagSize, 0, 4);
    263. this.TagSize = GetID3EncodedSizeV4(mTagSize).ToString();
    264. }
    265. void ParseHeaderV4()
    266. {
    267. byte[] mHeaderFlags = new byte[2];
    268. byte[] mTagSize = new byte[4];
    269. gTagReader.Read(mHeaderFlags, 0, 1);
    270. if (ExamineBit(mHeaderFlags[0], 8) == true) this.HeaderFlags = "Unsynchronization";
    271. if (ExamineBit(mHeaderFlags[0], 7) == true) this.HeaderFlags += ", " + "Extended Header";
    272. if (ExamineBit(mHeaderFlags[0], 6) == true) this.HeaderFlags += ", " + "Experimental Indicator";
    273. if (ExamineBit(mHeaderFlags[0], 5) == true) this.HeaderFlags += ", " + "Footer";
    274. gTagReader.Read(mTagSize, 0, 4);
    275. this.TagSize = GetID3EncodedSizeV4(mTagSize).ToString();
    276. }
    277. public static int GetID3EncodedSizeV2(byte[] argBytes)
    278. {
    279. return (argBytes[0] * 256 * 256 * 256) + (argBytes[1] * 256 * 256) + (argBytes[2] * 256) + argBytes[3];
    280. }
    281. public static int GetID3EncodedSizeV3(byte[] argBytes)
    282. {
    283. return (argBytes[0] * 256 * 256 * 256) + (argBytes[1] * 256 * 256) + (argBytes[2] * 256) + argBytes[3];
    284. }
    285. public static int GetID3EncodedSizeV4(byte[] argBytes)
    286. {
    287. return (argBytes[0] * 128 * 128 * 128) + (argBytes[1] * 128 * 128) + (argBytes[2] * 128) + argBytes[3];
    288. }
    289. public static bool ExamineBit(object MyByte, object MyBit)
    290. {
    291. short num = Conversions.ToShort(Operators.ExponentObject((object)2, Operators.SubtractObject(MyBit, (object)1)));
    292. return Operators.ConditionalCompareObjectGreater(Operators.AndObject(MyByte, (object)num), (object)0, false);
    293. }
    294. }
    295. }
    if Brain.Enabled = False Then
    Process.start("C:\Brain.exe")
    End if
    __________________________________________________

    Error: Brain.exe not found System shut down
    @Toni03 Also:
    Ich würde mal sagen, dass der Inhalt des Streams nicht komplett ist.
    Ich habe die Bytes als *.jpg auf Festplatte geschrieben, der IrfanView kommt damit klar, das Framework jedoch nicht:

    C#-Quellcode

    1. MemoryStream ms = new MemoryStream(argFrame.ValueBytes, mApicDataStart, argFrame.ValueBytes.Length - mApicDataStart);
    2. //img = Image.FromStream(ms);
    3. //return img;
    4. string temp = "c:\\Temp\\Testbild.jpg";
    5. System.IO.File.WriteAllBytes(temp, argFrame.ValueBytes);
    6. return Image.FromFile(temp);
    und in der zweiten Runde knallt es:

    C#-Quellcode

    1. public Form1()
    2. {
    3. this.InitializeComponent();
    4. string temp = "c:\\Temp\\Testbild.jpg";
    5. pictureBox1.Image = Image.FromFile(temp);
    6. }

    Möglicherweise ist der Stream einfach ein paar Bytes zu kurz.
    ====
    Wenn ich Deinen Output mit den vom MediaPlayer extrahierten Bildern vergleiche, fehlt da aber sehr viel.
    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!

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

    Also brauche ich mehr Arbeitsspeicher dafür, kann ich den irgendwie für das Programm freigeben? Oder wie könnte man das bewerkstelligen?
    if Brain.Enabled = False Then
    Process.start("C:\Brain.exe")
    End if
    __________________________________________________

    Error: Brain.exe not found System shut down

    Toni03 schrieb:

    Oder wie könnte man das bewerkstelligen?
    Du solltest aufklären, was da tatsächlich los ist.
    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!
    @Toni03 Hier ist mir ein kleiner Fehler unterlaufen, da wird der ApicDataStart = 7-Wert nicht berücksichtigt:

    RodFromGermany schrieb:

    C#-Quellcode

    1. System.IO.File.WriteAllBytes(temp, argFrame.ValueBytes);
    Allerdings kommt bei

    C#-Quellcode

    1. File.WriteAllBytes(temp, ms.GetBuffer());
    eine Exception:

    wozu bei Microsoft steht:

    C#-Quellcode

    1. // Ausnahmen:
    2. // System.UnauthorizedAccessException:
    3. // Die MemoryStream-Instanz wurde nicht mit einem öffentlich sichtbaren Puffer
    4. // erstellt.
    5. [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
    6. public virtual byte[] GetBuffer();
    Mit

    C#-Quellcode

    1. string temp = "c:\\Temp\\Testbild.jpg";
    2. int length = argFrame.ValueBytes.Length - mApicDataStart;
    3. byte[] buffer = new byte[length];
    4. Array.Copy(argFrame.ValueBytes, mApicDataStart, buffer, 0, length);
    5. File.WriteAllBytes(temp, buffer);
    haben wir zwar nun den richtigen Daten-Inhalt auf der Festplatte, der auch vom IrfanView korrekt gelesen wird, aber Paint lehnt das Format ab.
    Wenn ich mir nun den Hex-Dump Deines Outputs mit dem Hex-Dump einer "ordentlichen" JPEG-Datei ansehe:

    fällt der Prefix "JPEG 1" auf, der nicht Bestandteil des JPeg-Headers ist.
    Ändere ich nun im Code

    C#-Quellcode

    1. const int mApicDataStart = 7;
    in

    C#-Quellcode

    1. const int mApicDataStart = 15;// 7;
    redet die Nachbarin mit mir:

    und der ursprüngliche Code tut, was er soll:

    C#-Quellcode

    1. int length = argFrame.ValueBytes.Length - mApicDataStart;
    2. MemoryStream ms = new MemoryStream(argFrame.ValueBytes, mApicDataStart, length);
    3. return Image.FromStream(ms);
    :thumbsup:
    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!
    Ich hatte noch ein Problem, das ich lösen konnte, und zwar: Wenn ich den Code mit dem MemoryStream nutzte, kam die Fehlermeldung "Ungültiger Parameter" und bei dem anderen Code kam eine für mich nicht benutzbare Bilddatei raus. Dann habe ich die Hex Header von deinem Bild mit denen von meiner verglichen.



    Ich habe dann

    C#-Quellcode

    1. ​int mApicDataStart = 15;
    durch

    C#-Quellcode

    1. ​int mApicDataStart = 14;
    ersetzt und es hat geklappt. Danke! Du hast mir echt geholfen.
    if Brain.Enabled = False Then
    Process.start("C:\Brain.exe")
    End if
    __________________________________________________

    Error: Brain.exe not found System shut down
    @Toni03 Das ist dann ggf. eine Versionsfrage, also variabel.
    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!