Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1534959
  • 博文数量: 114
  • 博客积分: 10010
  • 博客等级: 上将
  • 技术积分: 1357
  • 用 户 组: 普通用户
  • 注册时间: 2006-11-19 18:13
文章分类
文章存档

2010年(8)

2009年(9)

2008年(27)

2007年(62)

2006年(8)

我的朋友

分类: LINUX

2007-08-09 00:09:17

忙活了好久,没有更新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
阅读(5271) | 评论(9) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2008-03-30 17:15:19

请问H264的解码是如何移植到A1200上的,是烧制上去的?什么软件可以烧制?是否可以做安装包呢?

chinaunix网友2008-03-30 17:15:19

请问H264的解码是如何移植到A1200上的,是烧制上去的?什么软件可以烧制?是否可以做安装包呢?