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

下面这部分代码可用之处为从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. 创建GIF loading图片

    第一步 新建一个宽80PX 高10PX的文档 第二步 做8个宽8PX的方格 黄色色值#e7a521 红色色值#ff0000(可根据自己的喜好设定) 第三步 复制7个层(共8个图层)每个图层相应改变红色 ...

  2. android 5.0新特性CardView教程

    CardView 是android5.0新加入的特性,大家先别着急,由于谷歌出了cardview的兼容包,也就是android.support.v7.widget.CardView包,所以在5.0以下 ...

  3. Docker 生成Node.js web app(含端口映射)

    1.新建目录src,并进入src目录 [xiejdm@localhost Documents]$ mkdir src [xiejdm@localhost Documents]$ cd src/ 2.创 ...

  4. GDAL 生成shp文件

    附件:http://pan.baidu.com/s/1i3GPwrV(C#版GDAL接口.dll) 示例程序: http://pan.baidu.com/s/1jpIKQ  (程序是在vs2008 x ...

  5. S​Q​L​ ​S​e​r​v​e​r​中​​的​P​W​D​E​N​C​R​Y​P​T​与​P​W​D​C​O​M​P​A​R​E​函​数

    前幾天有個客戶的網站出問題(不是我們開發的),請我們幫他看,主要的問題是他們的網站會員在進行查詢密碼時,會員收到信的時候在密碼的欄位竟然會出現 System.Binary[] 字樣.而我進去資料庫中查 ...

  6. Chapter 02:复合 VS 继承

    复合优先于继承,继承是实现代码重用的有力手段,并不是所有情况都适用,使用不当会导致软件变得很脆弱.与方法调用不同的是,继承打破了封装性. 总而言之,组合和继承,都能实现对类的扩展.但是要分具体情况用哪 ...

  7. plsql基本语法(

    1. 定义常量的语法格式    常量名 constant 类型标识符 [not null]:=值;    常量,包括后面的变量名都必须以字母开头,不能有空格,不能超过30个字符长度,同时不能和保留字同 ...

  8. SQL Server 内存管理在64位时代的改变

    64位机上  地址空间比以前大了去了.它引起的改变多了去了 1.MemToLeave这个词不存在了.因为SQL Server以不再做这种预留空间的事了,也就是说multiple page 想用多少就用 ...

  9. 【配置】电信华为HG8245 无线路由器配置 有贴图

          引子:家里的电信无线路由器连接之后无法直接上上网,只能再次通过PPPoe方式拨号上网.经过网上查询和一番折腾之后,整理攻略如下. 1. 用超级用户登录192.168.1.1(默认密码) 用 ...

  10. Unix主机syslog配置

    将下面的内容附加到/etc/syslog.conf文件中(注意*和@之间是有空格的): *.* @ip 修改/etc/services文件中的syslog服务的端口号为上面提到的Syslog监听端口. ...