symbian 3rd 流媒体FLV格式视频播放

北京理工大学  20981  陈罡

目前随着智能手机的不断发展、进化,手机功能越来越强大,处理器能力越来越高,配有200MHz,300MHz处理器的智能手机已经不是什么凤毛麟角,而是成为大众化的电子消费品了,这也为基于智能手机系统的第三方应用软件开发商带来了宝贵的发展机遇。眼看3G在即,未来手机上的平均带宽应该能够达到64K左右(这是从TD的角度来看的,如果采用WCDMA或者CDMA2000会更快一些),这远远超过了gprs传统的平均带宽9k的速度。手机应用开发领域应该会有一番“血战”,开发商会绞尽脑汁设计出各自的“杀手级”的应用程序吸引用户、丰富手机的功能,当然同时赚得铂满瓢满。:D
但是这一切都取决于是否能够创造出用户感兴趣的应用。我个人觉得,如果3G的带宽真的可以做得很大,一旦用户可以很轻易地从手机网络访问到互联网,相比于互联网网站而言,传统的free wap之类的网站无论从规模上还是内容丰富程度上都无法与互联网相媲美,而且内容同质化问题严重。互联网会对整个wap行业带来一个非常大的冲击。对于应用开发商来说也将面临同样的问题,我们举个例子:对于传统的wap browser开发商来说,互联网目前的css, web 2.0以及以youtube为代表的视频类网站为wap browser提出了更高的要求。这相当于对此类应用的开发商提出了更高的要求。

目前多数开发商都致力于手机上RSS类应用的开发,对于低带宽的gprs网络下这是无奈的事情。但是对于用户来说,单单是几条新闻、几张图片,这种模式无论从用户体验上,还是从用户粘度上都不足以吸引用户长期使用下去,通常面临的问题就是用户新鲜几天就删掉了。而且还要在后台做很多工作,如内容的准备,编辑,处理等等。既然如此,如果带宽真的宽了,干嘛不直接利用互联网的资源和用户粘度来做自己的事情呢?

symbian平台上如果能够支持youtube该有多好?于是从google上搜索了一下,果然找到了一个开源的项目MobiFLV。这位开发者已经开发MobiFLV有很长时间了,确实做了很多宝贵的工作。从代码上来看,他是采用移植的方案,从open source的ffmpeg中将libavcodec摘出来,并且把只与flv格式相关的代码移植到symbian平台上。目前经过测试,MobiFLV支持的FLV是h263+mp3或者mpeg4+mp3编码的视频格式,至于最新的支持h264的flv或者h263+aac格式的flv都是不支持的,这一点想要使用这个代码的朋友要特别注意了。

下面内容是通过一番google得到的,关于flv中采用的h263视频编码跟标准是 Sorenson h.263,因此,它与3gp文件中采用的h263格式的编码是有区别的:
1、视频tags的组成:
tag类型        0x09
tag数据大小     3个字节的视频数据大小
tag时间戳       3个字节tag数据应用的时间(毫秒)
tag时间戳扩展   1个字节的时间戳扩展,让时间戳变成4字节,本字节作为时间戳的高位.
streamID      3个字节的类id,总是0
2、视频tags的数据:
视频tags数据和swf文件格式中的VideoFrame是相似的.他们的数据是一样的
视频格式的数据的组成如下:
(1)帧类型 4bit
1: 关键帧keyframe(视频中的关键帧,数据存储的是整个画面完整的数据,可以提取它来生成图片)
2: 中间帧inter frame(关键帧之间的状态,不完整的画面数据,需要依靠前面帧的数据生成)
3: 可任意使用的中间帧disposable inter frame(H.263 only)
(2)视频编码id 4bit
2: Sorenson H.263(mencoder转换所使用的视频编码)
3: Screen video
4: On2 VP6
5: On2 VP6 with alpha channel
6: Screen video version 2
(3)视频数据
If CodecID = 2 Then H263VIDEOPACKET
If CodecID = 3 Then SCREENVIDEOPACKET
If CodecID = 4 Then VP6FLVVIDEOPACKET
If CodecID = 5 Then VP6FLVALPHAVIDEOPAC
If CodecID = 6 Then SCREENV2VIDEOPACKET
这里说一下Sorenson H.263视频编码以及其数据包:
从swf6开始,flash使用被称作Sorenson H.263的视频编码格式,这种格式基于h.263,一个公开视频编码标准由ITU(国际电信联盟)提出的.想了解h.263编码格式的朋友可以参考
http://www.chinavideo.org/index.php?option=com_content&task=view&id=123&Itemid=0
但是Sorenson H.263编码和H.263是有差别的:
下面的特性不存在Sorenson H.263中:
■ GOB (group of blocks) layer
■ Split-screen indicator
■ Document camera indicator
■ Picture freeze release
■ Syntax-based arithmetic coding
■ PB frames
■ Continuous-presence multipoint
■ Overlapped block-motion compensation
下面的特性是Sorenson H.263增加的:
■ Disposable frames (difference frames with no future dependencies)
■ Arbitrary picture width and height up to 65535 pixels
■ Unrestricted motion vector support is always on
■ A deblocking flag is available to suggest the use of a deblocking filter
关于这个MobiFLV,编译起来是非常方便的。可以使用code warrior或者carbide c++进行编译,我采用的sdk版本是symbian 3rd FP1,这里需要注意的是,MobiFLV是需要安装Open C插件的,而做为libavcodec的解码器本身是不需要的,这一点安排很有点讽刺意味,呵呵,真正有可能用到Open C的反而没有用到,而前端却又用到了。还有,似乎用vs2005+carbide.vs插件的编译环境不能够工作。另外,这个版本的MobiFLV是支持RVCT的,也就是说是可以用abld build armv5 urel来生成手机上的测试版本的,RVCT是支持thumb 16位指令集的,难以想象支持FLV视频的播放器编译出来的sis包,才53K多一点。笔者已经使用N81手机真机测试通过。

从代码结构上来看,MobiFLV主要分为两个部分:
(1)libavcodec_mobitubia(它是libavcodec解码器的核心部分)
完全是libavcodec中关于flv解码部分的修改和移植,这部分工作确实做得非常出色,没有内存泄露。

(2)MobiFLV(它是播放器的调用前端)
这里面需要重点关注Player.h扼Player.cpp,这里面详细(可以说是以教科书般的代码,展示了libavcodec的调用方法)相信如果仔细阅读了Player.cpp,关于“他是如何将视频数据变成图像的?”,类似这样的疑惑就全部一目了然了。

很多朋友对于视频播放器中如何同步视频和音频信息这块非常困扰,下面我就来结合代码做一个说明:

开始视频播放主要是在CMobiFLVAppView::OfferKeyEventL()函数中使用如下代码,开启了一个CPeriodic,来快速周期性的快速调用CMobiFLVAppView::PlayLoop():

iPeriodic = CPeriodic::NewL(0);
TInt tTickInterval = 1;
iPeriodic->Start(tTickInterval, tTickInterval, TCallBack(PlayLoopTick,this));

而这个PlayLoop的实现如下:

TInt result = FetchCodecAndDecode(iPts, iNeoStreamPlayer);
    if (result == -3)    // End
    {
        iPts = FLVGetDuration() * 1000;
        delete iPeriodic;
        iPeriodic = NULL;
    }


    iNowTime.HomeTime();
    iOffScreenBitmap->LockHeap();
    if (DoWithDecoded((unsigned char*)iOffScreenBitmap->DataAddress(),EFalse, 

iFirstFrameNum, iFirstFrameTimeStamp, 

iFrameNum, iNowTime.Int64()) >MAX_FRAME_SKIP)
    {
        iFirstFrameNum = iFrameNum;
      iNowTime.HomeTime();
        iFirstFrameTimeStamp = iNowTime.Int64();
        DoWithDecoded((unsigned char*)iOffScreenBitmap->DataAddress(),

EFalse, iFirstFrameNum, iFirstFrameTimeStamp, 

iFrameNum, iNowTime.Int64());
    }
    FetchCodecAndDecodeSoundOnly(iPts, iNeoStreamPlayer, iAudioCurrentFTell);
    iOffScreenBitmap->UnlockHeap();
    User::ResetInactivityTime();
    DrawNow();

注意,这里的关于时间处理的关键部分,就在于这个 DoWithDecoded函数了。

int DoWithDecoded(unsigned char* dst, unsigned char no_pic, 

  intfirst_frame_num, int first_frame_timestamp, 

  int& frame_num, intnow_timestamp)
{
    const int should_b_frame_num = first_frame_num + 

(now_timestamp -first_frame_timestamp) * 

flvGlobalVar.framerate * 0.000001;


    const int diff = frame_num - should_b_frame_num;
    
    if (diff > 0)

 return 0;
        
    if (!no_pic)
    {
        if (diff == 0)
        {
            if (g_RectWidth == AVIdx && g_RectHeight == AVIdy)
                ConvertYUV2BGRFast(avpict.data[0], avpict.data[1],

avpict.data[2], dst, avpict.linesize[0], 

AVIdy, AVIdx);
            else            
                ConvertYUV2BGRNeoFast(avpict.data[0], avpict.data[1],

avpict.data[2], dst, 

avpict.linesize[0], AVIdy, 

AVIdx, g_RectWidth,g_RectHeight);
            numframeskipped = 0;
        }
        else
        {
            numframeskipped++;
        }
    }

    is_decoded = false;
    frame_num++;

    return numframeskipped;
}


密切关注它的前两行:

should_b_frame_num = first_frame_num + (now_timestamp -first_frame_timestamp) * 

flvGlobalVar.framerate * 0.000001;

这一行用来根据读取出来framerate,帧率来计算每一帧需要停留多久,如果还没有到下一帧的播放时间,那么这里的diff就过不去,就会return。呵呵,这里就是音视频的同步的精华了,只要有了framerate,剩下的直接计算出来,随便用个timer控制时间即可,这当然也是由于h.263解码本身的速度比较快,不会引起timestamp都到了,但是还没有解完当前帧的情况,不需要做由于decoder引起的跳帧问题,这在h264中可能就有一些差别了。在此,还是比较简单可行的,代码也很清晰。

MobiFLV的剩余部分,都是调用Player.h中开放的函数了,我个人以为,这个所谓的Player API应该做成一个类比较符合symbian风格了。这里有一个文件不能不提,那就是YUVToRGB.h和YUVToRGB.cpp,作者用了这么个
ConvertYUV2BGRNeoFast(),
ConvertYUV2BGRNeoFastScaleUp(),
ConvertYUV2BGRNeoFastScaleDown()
呵呵,连黑客帝国中的Neo都用上了来取名字,自然表示了作者对这几个函数的自信。
执行效率自然很好,经过了大量的查表和手工位移优化,效率确实不错。
建议现在还在跟视频播放速度这块郁闷的朋友可以试试他弄的这个方法。

any way,能够工作就行。

下面是模拟器截图:
附上本文的源代码,由于MobiFLV的官方网站收到国内网关的影响,基本上登录不上去。需要用代理才可以访问,而这项技术又是国内开发者非常关注和需要的。我在此就做个好人,把MobiFLV 1.0的全部源代码贴出来供广大开发者使用:
文件: MobiFLV Pack 1.00.zip
大小: 285KB
下载: 下载
代码的编译和使用方法见代码包中的Release Note。顺便把我自己编译出来的没有签过名的sis包也贴出来,供感兴趣的朋友下载、测试。
文件: MobiFLV.rar
大小: 51KB
下载: 下载
注意,使用前一定要往手机C://Data//Videos//目录下面拷贝一个叫做Test.flv的视频,否则会Leave。

你可能感兴趣的:(symbian 3rd 流媒体FLV格式视频播放)