从MP3中读取专辑图片

好吧,最近帮朋友写毕设时碰到这个的问题的,在网上也是多番查询,都不尽人意,于是自己参考多番之后,写了一个

其实吧,这个读取专辑图片也不是很难得,首先判断MP3文件中是否含有ID3V2的标签,关于ID3V2的格式有一堆的说法

我嘛,不怎么关心,因此只攻专辑图片,也就是判断是否包含APIC这个标识

找到这个标识其实也就是和解析普通文件一样,每个像APIC标识的东西,都有帧标识头和帧标识内容组成,有头中得出帧内容的实际长度即可,然后将数据读出就行啦

但是对于APIC标识,读出了内容,直接保存为文件并不能显示图片,因为这个帧内容的前面一部分用于记录图片的类型,之后还有一部分空数据,这里就需要些判断

我用的mp3文件大部分都是jpeg的图片,在APIC帧内容中开头就是image/jpeg,这种内容,由于JPEG图片以0xFFD8开始,因此只要判断帧内容中以0xFFD8开始即可获取真正的图片数据,当然了,其他的数据格式,我就没有判断了,现在附上C++代码

#include <stdio.h> 
#include <stdlib.h> 
#include <memory.h>
#include <string.h>

int ReadAIPCFromMP3(const char* path);

int main(int argc, char **argv)
{
	ReadAIPCFromMP3("D:\\KuGou\\金海心 - 悲伤的秋千.mp3");
	return 0; 
}

bool isFrameAPIC(const char* cID3V2Fra_head)
{
	// 在win7中vs2012下,没发现strncasecmp函数,不然不需要两次判断
	// 当然了,这种判断也可以逐字节判断
	if ((strncmp(cID3V2Fra_head, "APIC", 4) == 0) ||
		(strncmp(cID3V2Fra_head, "apic", 4) == 0)) 
		return true;
	return false;
}

int calcID3V2Len(const char* cID3V2_head)
{
	int len = (cID3V2_head[6] & 0x7f) << 21
				| (cID3V2_head[7] & 0x7f) << 14
				| (cID3V2_head[8] & 0x7f) << 7
				| (cID3V2_head[9] & 0x7f);
	return len;
}

int calclID3V2FraLength(const char* cID3V2Fra_head)
{
	int len = (int)(cID3V2Fra_head[4] * 0x100000000 +
			cID3V2Fra_head[5] * 0x10000 +
			cID3V2Fra_head[6] * 0x100 +
			cID3V2Fra_head[7]);
	return len;
}

bool isJPEG(const char* data)
{
	// JPEG格式数据的起始为0xFFD8,当然了,结束也有标志,但是这里不需要了
	if (((unsigned char)data[0] == 0xFF) &&
		((unsigned char)data[1] == 0xD8))
		return true;
	return false;
}

int ReadAIPCFromMP3(const char* path)
{
	FILE* fp = fopen(path, "rb");
	if (!fp)
	{
		printf("cannot open this mp3\n");
		return -1;
	}

	// 这里的ID3V2以及帧标识的大小由标准规定均为10个字节
	// 这里其实应该是作为字节存储,用unsigned char更好,这里就简单用char替代吧
	char  cID3V2_head[10] = {0}, cID3V2Fra_head[10] = {0};
	long  ID3V2_len = 0, lID3V2Fra_length = 0;
	char* cID3V2Fra = NULL;

	// 读取帧头,这里就是为了判断是否是ID3V2的标签头
	fread(cID3V2_head, 10, 1, fp);
	if ((cID3V2_head[0] == 'I' || cID3V2_head[0] == 'i') &&
		(cID3V2_head[1] == 'D' || cID3V2_head[1] == 'd') &&
		cID3V2_head[2] == '3')
	{
		// 获取ID3V2标签的长度
		ID3V2_len = calcID3V2Len(cID3V2_head);
	}

	bool hasAPIC = false;
	// 这里来逐个读取帧标识,这里的专辑图片即保存在APIC标识里
	while ((ftell(fp) + 10) <= ID3V2_len)
	{
		// 这里每个帧标识的长度也为10,由于每个帧标识的存储的数据的长度不一
		// 每次要读取出来,进行运算获取真正数据长度
		memset(cID3V2Fra_head, 0, 10);
		fread(cID3V2Fra_head, 10, 1, fp);
		lID3V2Fra_length = (cID3V2Fra_head[4] * 0x100000000 +
			cID3V2Fra_head[5] * 0x10000 +
			cID3V2Fra_head[6] * 0x100 +
			cID3V2Fra_head[7]);
		
		// 这里判断是否为专辑图片的帧标识
		if (isFrameAPIC(cID3V2Fra_head))
		{
			cID3V2Fra = (char*)calloc(lID3V2Fra_length, 1);
			if (cID3V2Fra != NULL)
			{
				hasAPIC = true;
				fread(cID3V2Fra, lID3V2Fra_length, 1, fp);
			}
			break;
		}
		else
		{
			// 移动到下一帧标识
			fseek(fp, lID3V2Fra_length, SEEK_CUR);
		}
	}

	fclose(fp);

	// 到这里如果,专辑图片要么读出,要么就不存在
	if (hasAPIC)
	{
		// 这里整个数据的前面一部分数据是用来记录专辑图片的格式
		// 例如 image/jpeg image/png等,这里大部分的专辑图片都是jpeg的
		// 因此这里简单的只判断jpeg的格式,除去图片格式,数据前部依然有些是空数据
		// 因此以jpeg的标识来定位图片数据的起始
		int start = 0;
		while (start < lID3V2Fra_length)
		{
			if (isJPEG(cID3V2Fra + start))
				break;
			++start;
		}

		// 是以JPEG格式存在,则存储为jpeg的文件
		if (start != lID3V2Fra_length)
		{
			// 这里没有错误处理,从简
			FILE* jpegFP = fopen("test.jpeg", "wb");
			fwrite(cID3V2Fra + start, lID3V2Fra_length - start, 1, jpegFP);
			fclose(jpegFP);
		}
	}

	return 0;
}


  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值