📄 mp3.c
字号:
/**************************************************************************************************************mp3播放模块(由于交叉编译库不完备,在开发板上只能暂时只能播放mp2格式的文件)。该模块由4个主要函数构成:main(),mp3_play(),MPEG4_CtrlMessage()和ShowBMP()。main()负责加载开机画面,创建并管理两个线程。mp3_play()和MPEG4_CtrlMessage()分别是两个独立线程的入口函数,分别负责控制mp3的各个功能和获取用户命令信号。函数open_file()和set_audio()分别负责打开和设置音频设备文件。**************************************************************************************************************//*需要包含的头文件*/#include <SDL/SDL.h>#include <avcodec.h>#include <avformat.h>#include <stdio.h>#include <pthread.h>#include <unistd.h>#include "s3c2410_ts.h"#include <stdlib.h>#include <string.h>#include <math.h>#ifdef HAVE_AV_CONFIG_H#undef HAVE_AV_CONFIG_H#endif#include <SDL/SDL_audio.h>#include <assert.h>#include <sys/soundcard.h>#include <sys/stat.h>#include <fcntl.h>#include <sys/ioctl.h>#include <errno.h>#include <sched.h>/*预定义mp3文件的数目*/#define MusicNumber 3/*全局变量定义和初始化*/int STATE_EVENT = 3; //接受用户mp3机能控制命令的全局变量,初始值设为“停止”。int STATE_VOL = 2; //接受用户音量控制命令的全局变量,初始值设为“无效”。int volumn = 90; //音量。char audiofilename[MusicNumber][100]={"/qinbo/zuobian.mp2","/qinbo/juhuatai.mp2","/qinbo/HaoYanLei.mp2"}; //mp3文件名。/*函数声明*/void *mp3_play(void *junk);void *MPEG4_CtrlMessage(void *junk);void ShowBMP(char *file, SDL_Surface *screen,int x, int y);int open_file (char *file_name, int mode);int set_audio (int fd1, int fd2, AVCodecContext *pCodecCtx);/*按照mode设定的模式打开file_name指向的设备文件,并返回设备文件描述符信息*/int open_file (char *file_name, int mode){ int fd; if ((fd = open (file_name, mode)) < 0) // 打开文件和失败处理。 { fprintf (stderr, " Can't open %s!\n", file_name); exit (-1); } return fd; //并返回文件描述符信息。}/*根据传入的pCodecCtx变量,初始化音频设备的基本属性(为提高代码效率,可以嵌入到调用的地方)*/int set_audio (int fd1, int fd2, AVCodecContext *pCodecCtx){ int audioset; audioset = AFMT_S16_LE; ioctl (fd1, SNDCTL_DSP_SETFMT, &audioset); // 设置采样点格式为AFMT_S16_LE audioset = 2;//pCodecCtx->channels; // 设置声道,stereo:2 mono:1 ioctl (fd1, SNDCTL_DSP_CHANNELS, &audioset); audioset = 32000;//pCodecCtx->sample_rate; // 设置采样率 ioctl (fd1,SNDCTL_DSP_SPEED,&audioset); ioctl(fd2, MIXER_WRITE(SOUND_MIXER_VOLUME), &volumn); // 初始化音量设置 return 0;} /*主函数。负责加载开机画面,创建并管理两个独立线程*/int main(void){ pthread_t t_b; //定义线程标识 pthread_t t_c; SDL_Surface *screen = NULL; /*初始化SDL屏幕变量*/ screen = SDL_SetVideoMode (320, 240, 0, SDL_HWSURFACE); /*加载开机画面*/ ShowBMP("/qinbo/panasonic_mp2.bmp", screen,0, 0); /*创建两个线程*/ pthread_create(&t_b,NULL,mp3_play,(void*)NULL);//创建进程t_b pthread_create(&t_c,NULL,MPEG4_CtrlMessage,(void*)NULL);//创建进程t_c /*等待进程t_b结束*/ pthread_join(t_b, NULL); return 0; }/*线程t_c,用于拾取用户操作命令*/void *MPEG4_CtrlMessage(void *junk){ int fileno; //触摸屏设备文件描述符 int fangdou_flag =1; //防抖标志。1:处理触摸点信息许可 ,0:处理触摸点信息禁止 TS_EVENT ev; //触摸屏设备信息结构体/*初始化触摸屏结构体*/
memset(&ev, 0, sizeof(struct s3c2410_ts_event));
/*以O_RDONLY模式打开触摸屏设备文件,并定义失败处理*/
fileno = open("/dev/ts",O_RDONLY);
if (fileno == -1) {
printf("open device error!\n");
return NULL;
}
/*拾取和处理用户操作信息的循环体*/
for(;;) {/*读取一系列触摸点信息到*/
if(read(fileno, &ev, sizeof(struct s3c2410_ts_event))) { /*变换坐标*/ double i_tmp=2.984,j_tmp=3.708; ev.x=(unsigned int)((986-ev.x)/i_tmp); ev.y=(unsigned int)((948-ev.y)/j_tmp); /*只处理第一个触摸点信息*/ if ((ev.pressure)&&(fangdou_flag==1)) { //printf("x= %d, y= %d, flag=0x%04x\n", ev.x, ev.y, ev.pressure); if ((ev.y>=220)&&(ev.y<=240)) //功能控制区域 { if ((ev.x>=143)&&(ev.x<=190))STATE_EVENT=1; //播放开始区域,STATE_EVENT置1 else if((ev.x>250)&&(ev.x<=275)) STATE_EVENT = 2; //播放暂停区域,STATE_EVENT置2 else if((ev.x>50)&&(ev.x<=75)) STATE_EVENT = 3; //播放停止区域,STATE_EVENT置3 else if((ev.x>200)&&(ev.x<=245)) STATE_EVENT = 6; //播放下一首区域,STATE_EVENT置6 else if((ev.x>90)&&(ev.x<=130)) STATE_EVENT = 7; //播放上一首区域,STATE_EVENT置6 } else if((ev.y>=0)&&(ev.y<=20)) //音量控制区域 { if ((ev.x>=285)&&(ev.x<=320))STATE_EVENT=4; //退出区域,STATE_EVENT置4 else if((ev.x>21)&&(ev.x<=53)) STATE_VOL = 1; //减小音量区域,STATE_VOL置1 else if((ev.x>54)&&(ev.x<=85)) STATE_VOL = 0; //增大音量区域,STATE_VOL置0 } fangdou_flag = 0; //防止处理那些由于抖动所产生触摸点信息 } /*抖动信息屏蔽后,开启触摸点信息处理许可标志,等待下一个用户操作信息*/ if(ev.pressure == 0) fangdou_flag = 1; }
}
/*关闭触摸屏设备文件*/ close(fileno);
}void *mp3_play(void *junk){ /*FFMPEG 变量定义*/ AVFormatContext *pFormatCtx_audio; AVCodecContext *pCodecCtx_audio; AVCodec *pCodec_audio; AVPacket packet_audio; /*音频设备及其描述符定义和初始化*/ char *filename_audio = "/dev/dsp"; char *filename_volumn = "/dev/mixer"; int fd_audio = -1; int fd_volumn = -1; /*音频设备文件打开模式定义*/ int mode = O_WRONLY; /*解码器输出buffer*/ char *outbuf,*outbuf_tmp; /*其他与解码有关的变量定义和初始化*/ const int write_buffsize = 1024; //预定一次写入音频设备的字节数 int frameFinished_audio; //一次解码输出的字节数 int i, audioStream_audio = -1; //寻找音频流成功标识 int writelen = 0; //写入音频设备成功标识 int filenum = 0; //正在播放的曲目号码 //int frame_count=0; //帧数/*解码器和系统设备文件的初始化*/ fd_audio = open_file(filename_audio, mode); //打开音频设备文件,并返回描述符信息 fd_volumn = open_file(filename_volumn, mode); //打开音量设备文件,并返回描述符信息 /*初始化:注册所有解码器信息*/ av_register_all(); /*新选择的mp3功能控制的循环体*/while(1){ /*打开第filenum个音频文件,并定义失败处理*/ if(av_open_input_file(&pFormatCtx_audio, audiofilename[filenum], NULL, 0, NULL)!=0) { printf("File open failed!"); return NULL; } /*根据文件句柄寻找相关流信息,并定义失败处理*/ if(av_find_stream_info(pFormatCtx_audio)<0) { printf("Cannot find any stream information"); return NULL; } /*寻找音频流信息,并定义失败处理*/ audioStream_audio=-1; for(i=0; i<pFormatCtx_audio->nb_streams; i++)//寻找音频流信息的循环体 if(pFormatCtx_audio->streams[i]->codec.codec_type==CODEC_TYPE_AUDIO) { audioStream_audio=i; break; } if(audioStream_audio==-1) //定义失败处理 { printf("There is no audioStream"); return NULL; } pCodecCtx_audio=(&pFormatCtx_audio->streams[audioStream_audio]->codec); /*寻找相关的解码器,并定义失败处理*/ pCodec_audio=avcodec_find_decoder(pCodecCtx_audio->codec_id); if(pCodec_audio==NULL) { printf("There is no suitable decoder"); return NULL; } /*分配解码器空间*/ pCodecCtx_audio= avcodec_alloc_context(); /*打开解码器,并定义失败处理*/ if(avcodec_open(pCodecCtx_audio, pCodec_audio)<0) { printf("Cannot open the codec"); return NULL; } /*初始化音频设备*/ set_audio(fd_audio,fd_volumn,pCodecCtx_audio); /*动态分配解码输出空间*/ outbuf = (char *)malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE); /*当前mp3文件功能控制循环体*/while(1) { if(STATE_EVENT==1) //开始播放控制 { /*正常播放结束后,循环播放该首曲目*/ if(av_read_frame(pFormatCtx_audio, &packet_audio)<0) { av_free_packet(&packet_audio); //释放该包 break; } /*这是视频流中的一个包吗?*/ if(packet_audio.stream_index==audioStream_audio) { /* 解码视频流*/ avcodec_decode_audio(pCodecCtx_audio, (short *)outbuf, &frameFinished_audio, packet_audio.data, packet_audio.size); //set_audio(fd_audio,pCodecCtx_audio);//调用设置参数的函数,可以嵌入到此处。 //ioctl(fd_volumn, MIXER_WRITE(SOUND_MIXER_VOLUME), &volumn);// 设置音量 outbuf_tmp = outbuf; /*写音频设备*/ while ( frameFinished_audio > 0 ) { /*选择一次写入的字节数,1024或者frameFinished_audio*/ writelen = (frameFinished_audio > write_buffsize)?write_buffsize:frameFinished_audio; /*写音频设备,并返回成功写入的字节数*/ writelen = write(fd_audio, outbuf_tmp, writelen); if ( writelen == -1 ) //写失败处理 { printf("write audio device failed"); continue; } /*更新写buffer的起始位置*/ frameFinished_audio -= writelen; outbuf_tmp += writelen; } } /*释放音频包*/ av_free_packet(&packet_audio); }//IF if (STATE_EVENT == 2) //暂停播放 { // 空循环 } if (STATE_EVENT == 3)//停止播放 { /*返回文件起始位置*/ av_seek_frame(pFormatCtx_audio, -1,0); } if (STATE_EVENT == 4)//退出 { /*跳出该循环*/ break; } if (STATE_EVENT == 6)//播放下一首 { filenum++; //下一首 /*到最后一首则循环播放第一首*/ if(filenum >= MusicNumber) filenum = 0; /*模式设置为播放*/ STATE_EVENT = 1; /*跳出该层循环*/ break; } if (STATE_EVENT == 7)//播放上一首 { filenum--; //上一首 /*到第一首则循环最后一首*/ if(filenum < 0) filenum = MusicNumber - 1; /*模式设置为播放*/ STATE_EVENT = 1; /*跳出该层循环*/ break; } if (STATE_VOL == 0) //增加音量 { volumn += 5; //一次增加5个音量单位 ioctl(fd_volumn, MIXER_WRITE(SOUND_MIXER_VOLUME), &volumn); //设置音量设备 STATE_VOL = 2; //音量控制禁止 } if (STATE_VOL == 1)// 减少音量 { volumn -= 5; //一次减少5个音量单位 ioctl(fd_volumn, MIXER_WRITE(SOUND_MIXER_VOLUME), &volumn); //设置音量设备 STATE_VOL = 2; //音量控制禁止 } }//while /*如果是退出命令,则继续跳出最外层循环*/ if (STATE_EVENT == 4) { /*跳出该层循环*/ break; } /*关闭该首曲目的解码文件和释放空间*/ free(outbuf); //释放解码buffer avcodec_close(pCodecCtx_audio);//关闭解码器上下文 av_close_input_file(pFormatCtx_audio); //关闭文件上下文}//WHILE /*关闭音频设备,退出开机画面并结束线程*/ close (fd_audio); //关闭音频设备文件 close (fd_volumn);//关闭音量控制设备文件 SDL_Quit(); //退出开机画面 pthread_exit(0);//结束该线程}/***********************************************************这个子函数实现的功能是显示一幅位图,作为mp3播放器的开机画面。*************************************************************/void ShowBMP(char *file, SDL_Surface *screen,int x, int y) { if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) //初始化SDL { fprintf(stderr, "无法初始化SDL: %s\n", SDL_GetError());//初始化失败 exit(1); } if ( screen==NULL ) { //screen用于显示图片的基础外框 fprintf(stderr, "无法设置640x480的视频模式:%s\n", SDL_GetError());//无法设置640x480的视频模式,只能播放小于320*240的图片 exit(1); } SDL_Surface *image; SDL_Rect dest; /* 将BMP文件加载到一个surface*/ image = SDL_LoadBMP(file); if ( image == NULL ) { fprintf(stderr, "无法加载 %s: %s\n", file, SDL_GetError());//加载失败 return; } /* 初始化界面的长,宽,起始绘图点的坐标值, */ dest.x = x; dest.y = y; dest.w = image->w; dest.h = image->h; /*按照设定值显示图片*/ SDL_BlitSurface(image, NULL, screen, &dest); SDL_UpdateRects(screen, 1, &dest); SDL_FreeSurface(image);//释放image atexit(SDL_Quit);//退出}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -