一.
编写播放器的起因
在电视会议媒体问题诊断中,常常需要截取IP网络的流量并处理成原始码流以供播放器播放并根据播放效果诊断问题所在。该过程一般分为如下几个步骤:
1) 首先使用ethereal判断rtp流是否跟信令吻合,比如媒体类型,媒体封装格式以及媒体传输层地址等是否吻合。这一步主要是诊断出信令媒体配合问题以及互通问题。
2) 使用ethereal的RTP分析工具判断是否存在RTP包丢包或者严重的抖动情况,因为这些也会影响到电视会议的效果。这一部主要是诊断出网络问题。
3) 使用基于winpcap开发工具包编写的码流提取工具提取出纯码流,然后经过合适处理,送给pc解码器(图像可以用图像组的C03或者开源的ffplay等,音频可以用audacity)播放,观看播放效果。主要I帧的有无以及画质问题。这一部常用于诊断编解码器和主控配合问题。这一步因为牵涉比较多,下面列出需要注意的问题:
a) 对于H.261、H.263等码流,需要注意sbit和ebit的拼接,对于H.263+码流,按照规范简单处理就好了,对于H.264
码流,需要在每一个NAL单元前面添加0x00,0x00,0x00,0x01用于帮助解码器解码,注意,H.264
码流必须要有图像参数集PPS和序列参数集SPS后才能正确解码,对于没有必要参数集的,可以尝试配置一个默认的参数集。
b) 对于音频,可以使用一些辅助的库将G.722,G.728,G.723.1以及G.729转化成有符号16位的pcm然后播放,注意G.722的采样率是14K,如果按照8K播放,声音会很奇怪。在使用audacity的时候,可以先听出声音异常段,然后再看对应处的波形,看是否有丢包问题,最后再根据audacity显示的时间去截包文件中找到对应的包,看有无抖动过大或者丢包问题存在。
从以上罗列内容可以看出,码流的提取和播放需要一定的步骤和技巧,并且牵涉到了一些工具的使用,在使用ffmpeg或者mplayer的时候,一些参数的填写需要记忆,在audacity的导入对话框,也需要一定的码流知识,这些知识对于一般不是很熟悉媒体的技术人员来说很难掌握。基于这个目的,所以编写了一个集成的工具,该工具操作的对象是网络截包,然后基于传输地址自动把相关码流汇集起来,只要选择对应的码流,就可以播放出该码流。如果嵌入了QoS算法,那么还可以模拟出真实码流在该算法下丢包情况。比较适合一般工程人员初步定位问题使用。
二.
编写播放器的准备工作
1) GUI库的使用 使用wxWidgets用于界面显示
2) 解码库的使用 使用ffmpeg作为解码
3) 图形显示库的使用 使用了SDL作为图形显示
4) 截包解析库的使用 使用了winpcap作为操纵截包的库
5) 音频解码库的使用 使用了一个商业试用库,imtelephone公司的easy系列的库
6) 音频播放库的使用 使用了portaudio库播放声音
三.
播放器的大致思路
播放器工作的基本步骤:
1) 首先使用winpcap库读取截包文件,按照传输地址将码流分类读入内存
2) 根据一定的规则推测各个传输地址所包含码流的类型。如果是固定负载类型,那么直接给出类型,如果是动态负载类型,那么根据先验知识推断(目前也就是视频的h263+和h264,还是很容易判断出来的)。
3) 在用户选择了QoS模拟后,按照内存内保存的包的时间线索以及QoS调度时间,以步进方式模拟,对于每一个丢包,做一个适当的纪录,模拟结束后,输出汇总信息。
4) 如果用户选择播放,如果用户已经打开过截包文件,并且有过选择,那么需要询问用户是否播放选择的,或者需要打开磁盘上的文件播放。虽然有所区别,但是对于播放器来说就没有太大区别。下面按照两个分支分别叙述:
a) 如果是播放码流文件,需要注意的是要在送给解码器前预处理一下,对于视频,比如H.261和H.263的负载头的剥离和sbit、ebit的拼接,H.263+头部的简单变化,H.264的流头的添加。对于音频,在音频库需要数据的时候,需要解码出数据,然后在回调内提供给以音频播放。
b) 如果是播放磁盘文件,那么需要在一些无法确认的地方需要提示用户选择,比如G.723的5.3K和6.3K,有了用户确认后,也就很好处理了。
播放器编程中一些要点:
1) ffmpeg库的使用,比如库的初始化,如何启动特定解码器,如何获得解码后的帧,如何处理函数返回值,以及数据结构是如何包含图像数据的,只有了解这些,才能够顺利的解出图像。
2) 如何在wxPanel中绘制图像,主要是利用了SDL_Surface,将解码后的数据填写到SDL_Surface内,然后在重画的时候利用SDL_Surface内部数据生成缓冲的wxBitmap,然后直接绘制。
3) 在播放音频的时候要正确的理解采样率,单双声道以及采样数据类型,在回调函数中要能够正确的给缓冲区赋值
4) 在wxWidgets环境中正确的使用线程,由于图像的播放需要定时地驱动,所以要派生出线程来生成定时事件。同时线程间同步的处理也要谨慎,否则容易造成死锁或其他异常。