一、目标与思路
有了前面浏览页面的显示和功能实现,“连播模式”就可完成了。
本文目标:实现图片的连播
-->连播模式:如果没有设置,则默认从根目录/递归显示所有的图片(bmp和jpg格式)。
否则,如果设置了播放的“时间间隔” 和 选择的“播放目录”,
就会按设置的时间间隔递归连续显示那个目录下的所有图片。
如果实现呢?
我画了张连播页面的Run() 函数执行流程图,如下所示
下面根据这个流程图搭建整体实现,然后细讲每一方面的实现。
二、源码实现与效果图
脑海中一定要先理清上面流程图,然后看下面整体实现的搭建。
2.1 整体实现的搭建
/* 当点击主页面的“连播模式时”,主线程执行下面函数 */
static int g_iExitAutoPlay = 0; /* 是否退出子线程自动播放的标志位,默认肯定“否” */
static void HookupPageRun(void)
{
/* 别忘了这步!确保能重复播放 */
/* 因为是全局静态变量,初始化一次,程序结束才销毁 */
g_iExitAutoPlay = 0;
/* 1. 获得时间间隔和播放目录*/
GetPageCfg(&g_tPageCfg);
/* 2. 启动一个线程来连续显示图片 */
pthread_create(&g_tHookupPageThreadID, NULL, AutoPlayThreadFunction, NULL);
while(1)
{
/* 3. 等待触摸屏输入(主线程会休眠,有输入事件时,子线程会唤醒主线程) */
iRet = GetInputEvent(&tInputEvent);
if(iRet == 0)// 捕捉到触摸屏点击事件,返回0
{
/* 4. 唤醒后改变标志位让子线程退出播放 */
/* 因为标志位是线程共享的,修改时需加互斥锁 */
pthread_mutex_lock(&g_tAutoPlayThreadMutex);
g_iExitAutoPlay = 1;
pthread_mutex_unlock(&g_tAutoPlayThreadMutex);
/* 等待子线程退出,确保释放资源 */
pthread_join(g_tHookupPageThreadID,&thRet);
return;
}
}
}
/* 下面的子线程函数,实现按设定的时间间隔连续播放选定目录(或默认根目录)下的图片 */
static void* AutoPlayThreadFunction(void* pVoid)
{
int bExit;
int bFirst = 1;
while(1)
{
/* 1.获得互斥锁,并判断是否要退出播放 */
pthread_mutex_lock(&g_tAutoPlayThreadMutex);
bExit = g_iExitAutoPlay;
pthread_mutex_unlock(&g_tAutoPlayThreadMutex);
if(bExit)
break;
/* 2. 准备要显示的图片 */
ptVideoMem = PrepareNextPicture(0);
/* 3. 时间到了就显示出来 */
f(!bFirst)
{
sleep(g_tPageCfg.iIntervalSecond); /* 先用休眠来代替 */
}
bFirst = 0;
if(ptVideoMem == NULL)
{
/* 无论如何,肯定会准备好下一页面 */
ptVideoMem = PrepareNextPicture(1);
}
/* 4. 刷到设备上去并释放显存 */
FlushVideoMemToDev(ptVideoMem);
PutVideoMem(ptVideoMem);
}
return NULL;
}
框架就是上面那样,下面按顺序介绍每一步如何实现。
补充解释:
PrepareNextPicture(1)
PrepareNextPicture(0) 的区别?
0:以子线程的身份去获得显存的缓冲块,不一定获得成功。即返回的ptVideoMem 可能为NULL
1:当时间到时,如果ptVideoMem 还没准备好,就要以主线程身份去获得显存的缓冲块了,这一操作
肯定成功,因为迫在眉睫要显示下一张图片了呀!
另外:Hookup 是“连播”的意思。而韦老师选取的是“Auto”,两者都可以,我喜欢前者。
2.2 获得时间间隔和播放目录
1)GetPageCfg()函数,获得时间间隔和播放目录。实现如下
2.3 准备下一张图片
2)PrepareNextPicture() 函数,返回ptVideoMem,即指向已设置好显示下一张图片的缓冲块起始地址。
该函数的逻辑图看第一张图,下面列出该函数主体,并解释每一语句的作用。
2.4 递归获得目录下文件名
这部分代码很关键,所以一定要贴出来,如下
/* 以深度优先的方式获得目录下的文件
* 即: 先获得顶层目录下的文件, 再进入一级子目录A
* 先获得一级子目录A下的文件, 再进入二级子目录AA, ...
* 处理完一级子目录A后, 再进入一级子目录B
*
* "连播模式"下调用该函数获得要显示的文件
* 有两种方法获得这些文件:
* 1. 事先只需要调用一次函数,把所有文件的名字保存到某个缓冲区中
* 2. 要使用文件时再调用函数,只保存当前要使用的文件的名字
* 第1种方法比较简单,但是当文件很多时有可能导致内存不足.
* 我们使用第2种方法:
* 假设某目录(包括所有子目录)下所有的文件都给它编一个号
* piStartNumberToRecord : 从第几个文件开始取出它们的名字
* piCurFileNumber : 本次函数执行时读到的第1个文件的编号
* piFileCountHaveGet : 已经得到了多少个文件的名字
* iFileCountTotal : 总共要取出多少个文件的名字
*
*/
int GetFilesIndir(char *strDirName, int *piStartNumberToRecord, int *piCurFileNumber, int *piFileCountHaveGet, int iFileCountTotal, char apstrFileNames[][256])
{
int iError;
PT_DirContent *aptDirContents;
int iDirContentsNumber;
int i;
char strSubDirName[256];
#define MAX_DIR_DEEPNESS 10
static int iDirDeepness = 0; /*注意:静态局部变量,只初始化一次!*/
if(iDirDeepness > MAX_DIR_DEEPNESS) //递归终止的条件
{
return -1;
}
iDirDeepness++;
iError = GetDirContents(strDirName, &aptDirContents, &iDirContentsNumber);
if(iError)
{
DBG_PRINTF("GetDirContents error!\n");
iDirDeepness--;
return -1;
}
/* 先记录文件 */
for(i = 0; i < iDirContentsNumber; ++i)
{
if(aptDirContents[i]->eFileType == FILETYPE_FILE)
{
if(*piCurFileNumber >= *piStartNumberToRecord)
{
snprintf(apstrFileNames[*piFileCountHaveGet], 256, "%s/%s",
strDirName, aptDirContents[i]->strName);
(*piFileCountHaveGet)++;
(*piCurFileNumber)++;
(*piStartNumberToRecord)++;
if(*piFileCountHaveGet >= iFileCountTotal)
{
FreeDirContents(aptDirContents, iDirContentsNumber);
iDirDeepness--;
return 0;
}
}
else
{
(*piCurFileNumber)++;
}
}
}
/* 递归处理目录 */
for(i = 0; i < iDirContentsNumber; ++i)
{
if((aptDirContents[i]->eFileType == FILETYPE_DIR) &&
isRegDir(strDirName, aptDirContents[i]->strName))
{
snprintf(strSubDirName, 256, "%s/%s", strDirName, aptDirContents[i]->strName);
GetFilesIndir(strSubDirName, piStartNumberToRecord, piCurFileNumber,
piFileCountHaveGet, iFileCountTotal, apstrFileNames);
if(*piFileCountHaveGet >= iFileCountTotal)
{
FreeDirContents(aptDirContents, iDirContentsNumber);
iDirDeepness--;
return 0;
}
}
}
FreeDirContents(aptDirContents, iDirContentsNumber);
iDirDeepness--;
return 0;
}
2.5 效果图展示
备注:完整源码可到我的一篇“代码集合”博文获取百度网盘链接
OK,最后来看下效果图