忙活了好久,没有更新blog了,总算有点东西能够拿出来show show了。
这段时间主要在忙活把h264+amr_nb的解码器,以前在nokia s60的开发环境下面做的,现在应项目需要,把它移植到moto的a1200上面。根据前面的几篇文章积累下来的图形函数库和基本的操作方法,本次移植还是很顺利的。无非就是数据类型改一下,然后就是用posix的pthread替代原本的active object活动对象,后来在播放的时候遇到了时间的问题,原本的h264解码器是针对定码率10fps左右的QCIF(176x144)大小的视频进行播放的设计,在nokia的手机上采用了time critical的优先级别进行播放。视频和音频的时间同步必须非常准确,否则就会引起音频和视频脱节等问题。在a1200上,我尝试着用了很多方法均以失败告终,很是郁闷。
例如:
sleep(),不行,它是针对于秒级别的;
usleep()也不行,计时精度太差,而且是忙等待,对于手机的资源消耗很大。
而传统的signal+timer的方案也已经用于mp3播放了,此时再用timer会导致系统的稳定性变差,精度也有问题。。。
没有办法了,想到了gettimeofday函数,它据说可以得到微妙级别的计数。于是乎好好研究了一下,确实有所启发,当然了,首先需要包含两个头文件:
#include
#include //这个文件里面有timeval和timezone的结构定义
然后就是gettimeofday的函数定义了:
int gettimeofday ( struct timeval * tv , struct timezone * tz ) ;
函数说明
gettimeofday()会把目前的时间用tv所指的结构返回,当地时区的信息则放到tz所指的结构中。
timeval结构定义为:
struct timeval{
long tv_sec; /* 秒,就是普通的秒 */
long tv_usec; /*微秒,注意是1000000微妙等于1秒喔,1毫秒等于1000微秒!*/
};
timezone 结构定义为:
struct timezone{
int tz_minuteswest; /*和Greenwich 时间差了多少分钟*/
int tz_dsttime; /*日光节约时间的状态*/
};
上述两个结构都定义在/usr/include/sys/time.h文件内(a1200就没有了,呵呵,好在linux是到处都有的)。
tz_dsttime 所代表的状态如下
DST_NONE /*不使用*/
DST_USA /*美国*/
DST_AUST /*澳洲*/
DST_WET /*西欧*/
DST_MET /*中欧*/
DST_EET /*东欧*/
DST_CAN /*加拿大*/
DST_GB /*大不列颠*/
DST_RUM /*罗马尼亚*/
DST_TUR /*土耳其*/
DST_AUSTALT /*澳洲(1986年以后)*/
返回值
成功则返回0,失败返回-1,错误代码存于errno。附加说明EFAULT指针tv和tz所指的内存空间超出存取权限。
范例
#include
#include
main(){
struct timeval tv;
struct timezone tz;
gettimeofday (&tv , &tz);
printf(“tv_sec; %d\n”, tv,.tv_sec) ;
printf(“tv_usec; %d\n”,tv.tv_usec);
printf(“tz_minuteswest; %d\n”, tz.tz_minuteswest);
printf(“tz_dsttime, %d\n”,tz.tz_dsttime);
}
执行
tv_sec: 974857339
tv_usec:136996
tz_minuteswest:-540
tz_dsttime:0
在视频播放过程中,我们只关心两次gettimeofday的结果的差值,每次调用h264解码器设置在100毫秒左右的延时即可(10fps导致的)。可以利用sleep(0)来实现cpu的“空闲”延时:
我测试过sleep(n)代表延时n秒,这里如果n等于0,是不是会暂时释放一下cpu把时钟周期让给系统
别的程序使用呢?呵呵,关于这一点,还希望更多高手来拍砖。
void CM5H264Player::MainPlay()
{
bool play_over = false ;
// 这里是根据fps计算应该延时的长度,并且假设解码和缩放操作只用了1毫秒
int delay_ms = 1000 / (m_pMediaPlayer->GetVideoFPS() + 1) ;
CTimeStat tm_chk ;
tm_chk.ResetMs() ; // 重新设置相对时间起始点
while(!play_over && !m_need_stop) {
m_pContentManager->Update() ;
play_over = m_pMediaPlayer->Update() ;
if(play_over <= -1) {
(*m_callback_func)(m_callback_data) ; // 解码结束的回调函数,做扫尾工作的
break ;
}
// delay for a while nearly 91ms
while((int)(tm_chk.GetMs()) < delay_ms) sleep(0) ;
tm_chk.ResetMs() ;
}
}
这里的tm_chk的GetMs函数的定义如下:
unsigned long CTimeStat::GetMs()
{
unsigned long lft_ms = 0 ;
unsigned long sec_ms = 0 ;
if (m_state == TIME_NONE) return 0 ;
if (m_state == TIME_PAUSE) return 0 ;
if (m_state == TIME_CLOSE) return 0 ;
// 这里就是获取当前的时间了
gettimeofday(&m_curTime, &m_tz) ;
// 判断是否存在跨秒的比较问题,如果存在,则利用
// 已经过去的秒数减一
if(m_preTime.tv_usec > m_curTime.tv_usec) {
m_curTime.tv_usec += 1000000 ;
m_curTime.tv_sec-- ;
}
// 这里是计算毫秒
lft_ms = m_curTime.tv_usec - m_preTime.tv_usec ;
lft_ms /= 1000 ;
// 这里是计算秒,并且把秒转换成毫秒
sec_ms = m_curTime.tv_sec - m_preTime.tv_sec ;
sec_ms *= 1000 ;
// 最后返回结果
return (sec_ms + lft_ms);
}
相信如此一来,大家就对这个延时很了解了吧?通过上面的处理,线程可以基本精确的计时
并且满足多媒体视频播放的要求。网上看到别的大侠说,这样的方法在cpu比较空闲的时候
可能计时精度还可以,如果cpu满负荷,就会直接影响计时精度。我经过a1200的测试,发现
多数情况下是没有问题的,如果出现抢占cpu的情况,还可以酌情提高当前线程的优先级,
以期获得更多的cpu时间。
网上跟别的大侠讨论的结果通常是修改一下linux的驱动,这样可以做到完全准确的定时。然而
既然是给moto a1200开发,归根到底,我还是开发应用的,改了人家的驱动(尽管这也不是什么
mission impossible的事情,现在的linux手机连系统都可以随意刷,里面的包也自然可以随意修改)
似乎于情于理说不过去,还是保留原来的吧,至少可以通过上述办法变通的解决这个问题。
下面是视频播放的a1200真机抓图,在a1200的pxa270强大cpu下面,视频播放效果非常好:
相信不久,5m周刊就会完全在触摸屏的环境下运行起来的!
呵呵,最近iphone的gui给了我非常大的触动,这次在moto a1200手机上面,正好是触摸屏,我要试着实现一下看看,大家拭目以待吧 :P
阅读(5256) | 评论(9) | 转发(0) |