博客迁移后整理发型这篇文章当时没写完,不补了,不过还是得说明一些东西

下面这部分代码可用之处为从flac文件头开始然后各种形式的大跳,最后到达专辑封面的数据块,之后解析。

当时写的时候不会写图片解析部分,于是照搬了ShadowPlayer中某部分的代码,其有一特色为如果图片部分代码不认照样会爆搜然后尝试解析。实际上下面给出的代码的图片解析部分就是照搬的,而此处的正确做法恰恰不是如代码所示,我之前自己尝试能提取出图片的原因也就是爆搜成功了。。。

于是如果看官想要研究真正能用的代码,建议直接去看ShadowPlayer中此部分的代码。请参见这里

下面是之前写的原文:

这是代码(代码中的注释以及为了检查运行状态的奇怪提示没删,需要的话手动删除):

 /*
fLaC标签图片提取库 Ver 0.0
Gary 于2014/8/1 下午决定乱搞
*/ #ifndef _ShadowPowerOff_FLACPIC___
#define _ShadowPowerOff_FLACPIC___
#define _CRT_SECURE_NO_WARNINGS //安慰vs编译器用
#ifndef NULL
#define NULL 0
#endif
#include <cstdio>
#include <cstdlib>
#include <memory.h>
#include <cstring> typedef unsigned char byte;
using namespace std; namespace spFLAC {
//fLaC标签头部结构体定义
struct FLACHeader //似乎这就不用写成结构体咯,懒得改先用着
{
char identi[];//fLaC头部校验,必须为“fLaC”否则认为不存在fLaC标签
}; //fLaC标签METADATA_BLOCK_HEADER结构体定义
struct FLACMetaDataHeader
{ //MBFlagType共1bit+7bit=1byte
byte MBFlagType;//第一块1bit用于描述此MetaBlock是(1)不是(0)挨着音频块儿
//第二块7bit标志MetaBlock的种类的,其中6为PICTURE,别的用不着
byte size[]; //MetaBlock的大小,不包含 METADATA_BLOCK_HEADER大小
}; //按照官方文档的说法,图片块儿和id3v2的应该是一样的,下面直接照搬电影同志的代码
byte *pPicData = ; //指向图片数据的指针
int picLength = ; //存放图片数据长度
char picFormat[] = {}; //存放图片数据的格式(扩展名) //检测图片格式,参数1:数据,参数2:指向存放文件格式(扩展名)的指针,返回值:是否成功(不是图片则失败)
bool verificationPictureFormat(char *data)
{
//支持格式:JPEG/PNG/BMP/GIF
byte jpeg[] = { 0xff, 0xd8 };
byte png[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
byte gif[] = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 };
byte gif2[] = { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 };
byte bmp[] = { 0x42, 0x4d };
memset(&picFormat, , );
if (memcmp(data, &jpeg, ) == )
{
strcpy(picFormat, "jpg");
}
else if (memcmp(data, &png, ) == )
{
strcpy(picFormat, "png");
}
else if (memcmp(data, &gif, ) == || memcpy(data, &gif2, ) == )
{
strcpy(picFormat, "gif");
}
else if (memcmp(data, &bmp, ) == )
{
strcpy(picFormat, "bmp");
}
else
{
return false;
} return true;
} //安全释放内存
void freePictureData()
{
if (pPicData)
{
delete pPicData;
}
pPicData = ;
picLength = ;
memset(&picFormat, , );
} //将图片提取到内存,参数1:文件路径,成功返回true
bool loadPictureData(const char *inFilePath)
{
freePictureData();
FILE *fp = NULL; //初始化文件指针,置空
fp = fopen(inFilePath, "rb"); //以只读&二进制方式打开文件
if (!fp) //如果打开失败
{
fp = NULL;
return false;
}
fseek(fp, , SEEK_SET); //设文件流指针到文件头部 //读取
FLACHeader fLaCh; //创建一个FLACHeader结构体(即char[4] = "fLaC")
memset(&fLaCh, , ); //内存填0,4个字节
fread(&fLaCh, , , fp); //把文件头部4个字节写入结构体内存 //文件头识别
if (strncmp(fLaCh.identi, "fLaC", ) != )
{
fclose(fp);
fp = NULL;
return false;//没有fLaC标签
} //能运行到这里应该已经成功打开文件了
printf("是flac");
system("PAUSE"); FLACMetaDataHeader fLaCfh; //创建一个fLaCMetaBlockHeader结构体
memset(&fLaCfh, , ); //共4byte,第一个字节上面说过了,后3bit记录标签实际内容(不含头)大小 fread(&fLaCfh, , , fp); //将数据写到fLaCMetaBlockHeader结构体中
int curDataLength = ; //存放当前已经读取的数据大小,刚才已经读入4字节
while((fLaCfh.MBFlagType & 0x7F) != ) //如果标签不是6(即picture)则循环执行,
{
//计算帧数据长度
int frameLength = fLaCfh.size[] * 0x10000 + fLaCfh.size[] * 0x100 + fLaCfh.size[];
fseek(fp, frameLength, SEEK_CUR); //向前跳跃到下一个帧头
memset(&fLaCfh, , ); //清除帧头结构体数据
fread(&fLaCfh, , , fp); //重新读取数据
curDataLength += frameLength + ; //记录当前所在的ID3标签位置,以便退出循环
printf("刚刚打劫了⑨,没掉出图包\n");
if ((fLaCfh.MBFlagType & 0x80) == 0x80) return false;//不包含图片标签,完事.0x80 = 10000000
system("PAUSE");
printf("再来一次\n");
} printf("正在处理掉落");
//计算一下当前图片帧的数据长度
int frameLength = fLaCfh.size[] * 0x10000 + fLaCfh.size[] * 0x100 + fLaCfh.size[]; /*
这是ID3v2.3图片帧的结构: <Header for 'Attached picture', ID: "APIC">
头部10个字节的帧头 Text encoding $xx
要跳过一个字节(文字编码) MIME type <text string> $00
跳过(文本 + /0),这里可得到文件格式 Picture type $xx
跳过一个字节(图片类型) Description <text string according to encoding> $00 (00)
跳过(文本 + /0),这里可得到描述信息 Picture data <binary data>
这是真正的图片数据
*/
int nonPicDataLength = ; //非图片数据的长度
fseek(fp, , SEEK_CUR); //信仰之跃
nonPicDataLength++; char tempData[] = {}; //临时存放数据的空间
char mimeType[] = {}; //图片类型
int mimeTypeLength = ; //图片类型文本长度 fread(&tempData, , , fp);//取得一小段数据
fseek(fp, -, SEEK_CUR); //回到原位 strcpy(mimeType, tempData); //复制出一个字符串
mimeTypeLength = strlen(mimeType) + ; //测试字符串长度,补上末尾00
fseek(fp, mimeTypeLength, SEEK_CUR); //跳到此数据之后
nonPicDataLength += mimeTypeLength; //记录长度 fseek(fp, , SEEK_CUR); //再一次信仰之跃
nonPicDataLength++; int temp = ; //记录当前字节数据的变量
fread(&temp, , , fp); //读取一个字节
nonPicDataLength++; //+1
while (temp) //循环到temp为0
{
fread(&temp, , , fp); //如果不是0继续读一字节的数据
nonPicDataLength++; //计数
}
//跳过了Description文本,以及末尾的\0 //非主流情况检测
memset(tempData, , );
fread(&tempData, , , fp);
fseek(fp, -, SEEK_CUR); //回到原位
//判断40次,一位一位跳到文件头
bool ok = false; //是否正确识别出文件头
for (int i = ; i < ; i++)
{
//校验文件头
if (verificationPictureFormat(tempData))
{
ok = true;
break;
}
else
{
//如果校验失败尝试继续向后校验
fseek(fp, , SEEK_CUR);
nonPicDataLength++;
fread(&tempData, , , fp);
fseek(fp, -, SEEK_CUR);
}
} if (!ok)
{
fclose(fp);
fp = NULL;
freePictureData();
return false; //无法识别的数据
}
//-----真正的图片数据-----
picLength = frameLength - nonPicDataLength; //计算图片数据长度
pPicData = new byte[picLength]; //动态分配图片数据内存空间
memset(pPicData, , picLength); //清空图片数据内存
fread(pPicData, picLength, , fp); //得到图片数据
//------------------------
fclose(fp); //操作已完成,关闭文件。 return true;
} //取得图片数据的长度
int getPictureLength()
{
return picLength;
} //取得指向图片数据的指针
byte *getPictureDataPtr()
{
return pPicData;
} //取得图片数据的扩展名(指针)
char *getPictureFormat()
{
return picFormat;
} bool writePictureDataToFile(const char *outFilePath)
{
FILE *fp = NULL;
if (picLength > )
{
fp = fopen(outFilePath, "wb"); //打开目标文件
if (fp) //打开成功
{
fwrite(pPicData, picLength, , fp); //写入文件
fclose(fp); //关闭
return true;
}
else
{
return false; //文件打开失败
}
}
else
{
return false; //没有图像数据
}
} //提取图片文件,参数1:输入文件,参数2:输出文件,返回值:是否成功
bool extractPicture(const char *inFilePath, const char *outFilePath)
{
FILE *fp = NULL; //初始化文件指针,置空
if (loadPictureData(inFilePath)) //如果取得图片数据成功
{
if (writePictureDataToFile(outFilePath))
{
return true; //文件写出成功
}
else
{
return false; //文件写出失败
}
}
else
{
return false; //无图片数据
}
freePictureData();
}
}
#endif

调用方法(手动指定输入文件路径和输出文件路径,输出文件的格式自己猜吧~ gcc编译运行测试成功):

 #include "fLaCPic.h"

 int main(int argc, char* argv[])
{
using namespace spFLAC;
if (argc > )
{
extractPicture(argv[], argv[]);
}
else
{
printf("参数数量不足");
}
return ;
}

以上代码基于Shadow Player的ID3v2Pic.h头文件改造编写而成。由于flac格式的官方给出的说明文档上有说图片部分和id3v2是一样的所以那部分直接照搬了,注释也没改。

flac文件提取专辑封面手记的更多相关文章

  1. 用java获取歌曲文件的专辑封面元信息

    几个个软件: 1, Jaudioatgger: 链接 2, mp3agic 链接 3, Java mp3 id3 tag library  (推荐用上面两个) 其它: android-midi-lib

  2. Use GraceNote SDK in iOS(一)通过序列化GDO查询专辑封面

    于Use MusicBrainz in iOS之后,因为MusicBrainz找出专辑封面,它只能转移到其他网站提供的音乐信息搜索服务,领导给出GraceNote.(有压力.. .) 需求类似:通过一 ...

  3. Python爬虫小白入门(六)爬取披头士乐队历年专辑封面-网易云音乐

    一.前言 前文说过我的设计师小伙伴的设计需求,他想做一个披头士乐队历年专辑的瀑布图. 通过搜索,发现网易云音乐上有比较全的历年专辑信息加配图,图片质量还可以,虽然有大有小. 我的例子怎么都是爬取图片? ...

  4. webpack4对第三方库css,项目全局css和vue内联css文件提取到单独的文件(二十二)

    在讲解提取css之前,我们先看下项目的架构如下结构: ### 目录结构如下: demo1 # 工程名 | |--- dist # 打包后生成的目录文件 | |--- node_modules # 所有 ...

  5. 如何使用千千静听为MP3添加专辑封面和文字信息

    使用千千静听播放器打开某MP3文件,右击该文件,选择属性. 2 点击专辑封面即可添加或更换专辑封面 点击保存到文件再点击重新读取文件即可发现有效了 3 为MP3批量添加添加封面 选中播放列表的所有文件 ...

  6. C#读取MP3文件的专辑图片和ID3V2Tag信息(带代码)

    第二次更新,后面的代码有问题,有些专辑图片读取不到.发现是PNG图片的问题.在读取的过程中调试发现,图片帧前10个字节包含了图片的格式,在有些歌曲写着JPEG的格式,数据却是PNG的.先说下思路. j ...

  7. 苹果IPSW文件提取软件

    ipsw文件 提取系统文件 方法总结 由于修改运营商文件造成我的有锁4S无法使用移动卡了,在网上苦寻一番还是没有结果,最后萌生了从固件中提取文件的想法,于是便开始在网上搜集资料,最后文件终于提取成功并 ...

  8. 用MT.exe将exe中的manifest文件提取出来和将manifest文件放入exe中

     前一种方法是将manifest文件放入exe中,但是要记得需要在工程中设置 这样的话exe中就不存在manifest了,在debug目录下就会看到相应的manifest文件.后者是将exe中的man ...

  9. 音乐ID3 中 专辑封面解析(APIC帧)

    ID3V2 中 APIC 帧标识 专辑封面.前几天 百度 谷歌 都没有找到具体的说明.有点小伤人. 最好参考  Android 中的 id3.cpp 以及一个java 开源 id3 库.找到这里的规格 ...

随机推荐

  1. (转)Server Tomcat v6.0 Server at localhost was unable to start within 45 seconds

    仰天长啸 Server Tomcat v6.0 Server at localhost was unable to start within 45 seconds... 当启动tomcat时候出现 S ...

  2. 不同css样式适应不同屏幕大小实现自适应宽度

    @media screen and (判断属性){ CSS样式选择器 } 例如: <div class="silder_photo"></div> < ...

  3. Mongodb 安装和启动

    一.首先去官网下载对应的的mongodb ,本人的操作系统是win7 64位 mongodb-win32-x86_64-2.0.6.rar 解压安装:进入到bin目录下,会看到N多的.exe文件 二. ...

  4. 淘宝api 开发_获取用户信息

    string url = "http://gw.api.taobao.com/router/rest"; "; string appsecret = "fdf7 ...

  5. jquery跨域访问解决方案(转)

    客户端“跨域访问”一直是一个头疼的问题,好在有jQuery帮忙,从jQuery-1.2以后跨域问题便迎刃而解.由于自己在项目中遇到跨域问题,借此机会对跨域问题来刨根问底,查阅了相关资料和自己的实践,算 ...

  6. javascript模式

    http://developer.51cto.com/art/201212/372725.htm http://justjavac.com/javascript/2012/12/14/model-vi ...

  7. COB對PCB設計的要求

    由於COB沒有IC封裝的leadframe(導線架),而是用PCB來取代,所以PCB的焊墊設計就便得非常的重要,而且Fihish只能使用電鍍金或是ENIG(化鎳浸金),否則金線或是鋁線,甚至是最新的銅 ...

  8. 什麼是 N-key 與按鍵衝突?原理說明、改善技術、選購注意完全解析

    不管是文書處理或遊戲中,我們都經常會使用到組合鍵,也就是多顆按鍵一起按下,執行某些特定的功能.有時候你可能會發現,明明只按下2顆鍵,再按下第3顆鍵時訊號卻沒有輸出.要是打報告到一半遇到這種狀況還好,如 ...

  9. Arduino红外遥控系列教程2013——红外转码

    教程三:红外转码教程——用电视遥控器取代机顶盒遥控器 前言前段时间看到了使用红外遥控的X-Bot机器人[链接],感觉很有意思.最近开始玩Arduino与红外,一方面打算将来用于BOXZ的之间的通讯控制 ...

  10. Visual Studio 2015 Update 3 RC 候选预览版粗来了

    .Net 基金会 http://www.dotnetfoundation.org/ 更新的真快,刚打完2的补丁包,3就粗来了............ https://www.visualstudio. ...