網上的資料良莠不齊

TonyDeng UID.2870126
2018-12-09 发表

本帖最后由 TonyDeng 于 2018-12-9 13:12 编辑

最近研究了MP3文檔的格式,在網上搜索資料,很多來源於CSDN的,幾乎都指向同一個出處,但那些資料卻竟然有問題的。大的方向、文字性的描述,基本不錯,但具體到細節和給出的代碼,就有問題,迫使我根據那些文字描述,自己設計測試檢驗,才最終把情況弄清。由此我覺得,很多人說上網搜解答,估計比較懸。何況那麽多人回復和探討,也有人提出了疑問,卻不見作者回復正確的解答,不了了之,但現實又很多程序能夠正常使用,估計都是抄現成的代碼或拿庫,沒有人自己研究弄清究竟的。 舉個例子,MP3文檔的頭標簽,裏面記載標簽幀總大小的那個解碼公式,網上資料就是錯的,但文章卻說自己的驗證正確,還拿一個MP3歌曲來具體示例分析,然而我用他這個公式去算自己保存的MP3音樂,卻總是錯誤的結果(最終的算法不是他那樣,其實也很簡單,逐個移位罷了)。這是一例,其實裏面還有若干很陰的陷阱,如果不是分析足夠多不同的MP3文檔,根本不可能發現,要寫出完善解讀的程序,照他們那樣,肯定是不行的。 下圖是網上的資料,大多源自這篇文章: ***图片停止解析*** 下圖是我最後的結果,這個才是對的: ***图片停止解析***

敬告:
为防止不可控的内容风险,本站已关闭新用户注册,新贴的发表及评论;
你现在看到的内容只是互联网用户曾经发表的言论快照,仅用于老用户留存纪念,且仅与科技行业相关,全部内容不代表本站观点及立场;
本站重新开放前已针对包括用户隐私、版权保护、信息安全、国家政策在内的各种互联网法律法规要求,执行了隐患内容的自查、屏蔽和删除;
本站目前所属个人主体,未有任何盈利安排与计划,且与原WFUN.COM所属公司不存在任何关联关系;
如果本帖内容或者相关资源侵犯到您的合法权益,或者您认为存在问题,那么请您务必点此举报或投诉!
全部回复:
TonyDeng UID.2870126
2018-12-10 回复

續:現在把讀和寫都搞定了,可以隨便編輯MP3的標簽信息,增、查、刪、改都可以,包括向裏面添加專輯封面/封底圖和内嵌歌詞。

南****珏 UID.2988418
2018-12-10 回复

嗯,楼主的代码可以给我看一下吗

TonyDeng UID.2870126
2018-12-10 回复

Quote南宫珏 发表于 2018-12-10 16:14
嗯,楼主的代码可以给我看一下吗


你想要哪一塊?正在重構和完善中,原理可以解釋一下,這個論壇發代碼的功能不好看。

TonyDeng UID.2870126
2018-12-10 回复

本帖最后由 TonyDeng 于 2018-12-10 21:10 编辑

Quote南宫珏 发表于 2018-12-10 16:14 嗯,楼主的代码可以给我看一下吗
貼一個試試: public sealed class Header { public static int TagSize => 10; // 標簽尺寸 public bool IsValid => (ID == “ID3“) && (Ver == 3) && (Size > 0); // 標識是否有效的MP3V2.3文檔 public string ID { get; private set; } // MP3文檔的標識,必須為“ID3“ public byte Ver { get; private set; } // 版本號,若為3則表示V2.3版本 public byte Revision { get; private set; } // 副版本號,通常為零 public byte Flag { get; private set; } // 附加標識,bit[0]為非同步編碼標識,bit[1]為擴展標簽頭標識,bit[2]為測試標識 public int Size { get; set; } // 頭部數據尺寸 public Header(FileStream stream) { byte[] buffer = new byte[TagSize]; if (stream.Read(buffer, 0, TagSize) == TagSize) { ID = Encoding.ASCII.GetString(buffer, 0, 3); Ver = buffer[3]; Revision = buffer[4]; Flag = buffer[5]; Size = (buffer[6] << 7 << 7 << 7) + (buffer[7] << 7 << 7) + (buffer[8] << 7) + buffer[9]; } } private byte[] CreateBytes() { byte[] data = new byte[TagSize]; Encoding.ASCII.GetBytes(ID, 0, ID.Length, data, 0); data[3] = Ver; data[4] = Revision; data[5] = Flag; data[6] = (byte)((Size >> 7 >> 7 >> 7) & 0x7F); data[7] = (byte)((Size >> 7 >> 7) & 0x7F); data[8] = (byte)((Size >> 7) & 0x7F); data[9] = (byte)(Size & 0x7F); return data; } public void Write(FileStream stream) { if (stream.CanWrite) { byte[] bytes = CreateBytes(); stream.Write(bytes, 0, bytes.Length); } } } public sealed class FrameTag { public static int TagSize => 10; // 標簽尺寸 public string ID { get; set; } // 標簽名,4字符的ASCII文本 public int Size { get; set; } // 幀體大小,4位整數 public short Flag { get; set; } // 標識 public FrameTag(FileStream stream) { byte[] buffer = new byte[TagSize]; if (stream.Read(buffer, 0, TagSize) == TagSize) { ID = Encoding.ASCII.GetString(buffer, 0, 4); Size = (buffer[4] << 8 << 8 << 8) + (buffer[5] << 8 << 8) + (buffer[6] << 8) + buffer[7]; Flag = (short)((buffer[8] << 8) + buffer[9]); } } private byte[] CreateBytes() { byte[] data = new byte[TagSize]; Encoding.ASCII.GetBytes(ID, 0, ID.Length, data, 0); data[4] = (byte)(Size >> 8 >> 8 >> 8); data[5] = (byte)(Size >> 8 >> 8); data[6] = (byte)(Size >> 8); data[7] = (byte)Size; data[8] = (byte)(Flag >> 8); data[9] = (byte)Flag; return data; } public void Write(FileStream stream) { if (stream.CanWrite) { byte[] bytes = CreateBytes(); stream.Write(bytes, 0, bytes.Length); } } } public sealed class Picture { public string MIME { get; set; } // 圖像類型 public byte ID { get; set; } // 圖像編碼 public byte[] Data { get; set; } // 圖像的二進制數據 public Picture(byte[] data) { int startIndex = 1; // 此信息必定是ASCII編碼,data[0]必定是零,故直接從1開始搜索即可 MIME = MyTools.GetCString(Encoding.ASCII, data, ref startIndex); ID = data[startIndex]; startIndex += 2; Data = new byte[data.Length - startIndex]; Array.Copy(data, startIndex, Data, 0, Data.Length); } } public sealed class Frame { public FrameTag Tag { get; set; } public byte[] Data { get; set; } public string Content { get; set; } public Picture APic { get; set; } public Frame(FileStream stream) { Tag = new FrameTag(stream); if (Tag.Size > 0) { Data = new byte[Tag.Size]; stream.Read(Data, 0, Tag.Size); if (Tag.ID != “APIC“) { Content = ““; int startIndex = FindUnicodeStartIndex(); switch (Data[0]) { case 0: Content = MyTools.GetCString(Encoding.ASCII, Data, ref startIndex); break; case 1: Content = MyTools.GetCString(Encoding.Unicode, Data, ref startIndex); break; case 2: Content = MyTools.GetCString(Encoding.BigEndianUnicode, Data, ref startIndex); break; case 3: // V2.4 保留用於UTF-8 break; default: break; } } else { APic = new Picture(Data); Content = APic.MIME; } } } // 對Unicode編碼,有引導符,普通為FFFE,但也有其他外文語言有更多的引導符,故須找出FFFE後的位置 private int FindUnicodeStartIndex() { int startIndex = 1; if (Data[0] != 0) { for (int index = startIndex; index < Data.Length - 1; ++index) { if (Data[index] == 0xFF && Data[index + 1] == 0xFE) { startIndex = index + 2; break; } } } return startIndex; } public void SetContent(string content) { byte[] bytes = Encoding.Unicode.GetBytes(content); Data = new byte[3 + bytes.Length]; Data[0] = 1; Data[1] = 0xFF; Data[2] = 0xFE; Array.Copy(bytes, 0, Data, 3, bytes.Length); Content = content; Tag.Size = Data.Length; } public void Write(FileStream stream) { if (stream.CanWrite && Data.Length > 0) { stream.Write(Data, 0, Data.Length); } } } public sealed class MP3 { public Header Head { get; set; } public List<Frame> Frames { get; set; } = new List<Frame>(); public byte[] Audio { get; set; } public MP3(FileStream stream) { if (stream.CanSeek && stream.Seek(0L, SeekOrigin.Begin) == 0L) { Head = new Header(stream); if (Head.IsValid) { do { Frame frame = new Frame(stream); Frames.Add(frame); } while (Frames[Frames.Count - 1].Tag.Size > 0); Audio = new byte[(int)stream.Length - Head.Size - Header.TagSize]; if (stream.Read(Audio, 0, Audio.Length) != Audio.Length) { Audio = null; } } } } private int GetHeadSize() { int size = 0; foreach (Frame frame in Frames) { size += FrameTag.TagSize + frame.Tag.Size; } return size; } public void UpdateFrames(FileStream stream) { if (stream.CanWrite && stream.CanSeek) { stream.Seek(0L, SeekOrigin.Begin); Head.Size = GetHeadSize(); Head.Write(stream); foreach (Frame frame in Frames) { frame.Tag.Write(stream); if (frame.Tag.Size > 0) { frame.Write(stream); } } stream.Write(Audio, 0, Audio.Length); } } } 用法是類似這樣: FileInfo file = new FileInfo(fileName); using (FileStream stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)) { MP3 mp3 = new MP3(stream); if (mp3.Head.IsValid) { //DeleteFrame(mp3, "POPM"); //DeleteFrame(mp3, "TXXX"); foreach (Frame frame in mp3.Frames) { if (frame.Tag.Size > 0) { WriteLine($"{frame.Tag.ID} = [{frame.Content}]"); } } //mp3.UpdateFrames(stream); WriteLine(); } }

零三****岳志 UID.3005672
2019-03-15 回复

哈哈!还有灌水区呀!

本站使用Golang构建,点击此处申请开源鄂ICP备18029942号-4联系站长投诉/举报