关于VideoView延时播放的问题

jayson888 2011-08-19 08:39:44
我在一台手机上完成了264的硬编码,并打成RTP包,通过RTSP交互后,实时发送给另一台手机的Videoview,VideoView能收到并播放,但问题是,它需要有约5秒得延时才能播放,就是说我发送了5秒后,VideoView才有画面,图像质量倒是很不错,但5秒得延时实在太长了。开始我以为是我哪个环节没写对,但是我给VLC发就没有延时,所以我想VideoView因为是RTSP播放器,它可能需要缓冲5s的数据,但我也不敢肯定。请问做过VideoView的高手是否遇到同样的情况?是如何解决的?
...全文
1386 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
halleyzhang3 2013-03-26
  • 打赏
  • 举报
回复
我知道。没分了吗?
tongxingzheng123 2012-04-23
  • 打赏
  • 举报
回复
楼主你解决了没?我现在也遇到同样的问题求解啊
forever_crying 2011-08-20
  • 打赏
  • 举报
回复
太厉害了
jayson888 2011-08-20
  • 打赏
  • 举报
回复
最后还是考虑移植解码库,结贴了,给分
jayson888 2011-08-19
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 fontlose 的回复:]
系统默认使用固定大小来缓冲的 我这定义200000字节


const size_t kLowWaterMarkBytes = 40000;
const size_t kHighWaterMarkBytes = 200000;

if ((mFlags & PLAYING) && !eos
&& (cachedDataRemai……
[/Quote]

谢谢你的代码,如果没有其他办法了在考虑单独编译mediaplayer
jayson888 2011-08-19
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 fontlose 的回复:]
引用 9 楼 jayson888 的回复:
还有个问题,如果提取出mediaplayer,单独编译,能否在源码里去掉缓冲这一部分,不知道你了不了解?



源码在frameworks\base\media\libmedia我看一下先

mediaplayer的是模拟视频流播放的 要边下边播放 这里有个帖子
http://www.360doc.com/content/11/0303/……
[/Quote]

辛苦了,谢谢你,刚才看了这篇帖子,它的核心思想是区分了Streaming和Progress Download,按照它的定义,VideoView默认的需要缓冲方式是Progress Download,而我需要的是Streaming方式,他说如果在发送的mp4或者3gpp流里加入hint track,VideoView会自动解析出它是Streaming的,但是我发的不是文件流,我发的是264码流,里面没有hint信息,刚才在网上搜发现一篇论文,说法是,在RTSP协商的时候可通过SDP参数写hint信息,不知道可行否,我还得仔细看看
fontlose 2011-08-19
  • 打赏
  • 举报
回复
系统默认使用固定大小来缓冲的 我这定义200000字节


const size_t kLowWaterMarkBytes = 40000;
const size_t kHighWaterMarkBytes = 200000;

if ((mFlags & PLAYING) && !eos
&& (cachedDataRemaining < kLowWaterMarkBytes)) {
LOGI("cache is running low (< %d) , pausing.",
kLowWaterMarkBytes);
mFlags |= CACHE_UNDERRUN;
pause_l();
notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START);
} else if (eos || cachedDataRemaining > kHighWaterMarkBytes) {
if (mFlags & CACHE_UNDERRUN) {
LOGI("cache has filled up (> %d), resuming.",
kHighWaterMarkBytes);
mFlags &= ~CACHE_UNDERRUN;
play_l();
notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END);
} else if (mFlags & PREPARING) {
LOGV("cache has filled up (> %d), prepare is done",
kHighWaterMarkBytes);
finishAsyncPrepare_l();
}
}
fontlose 2011-08-19
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 jayson888 的回复:]
引用 6 楼 fontlose 的回复:
引用 4 楼 jayson888 的回复:
引用 3 楼 fontlose 的回复:
Videoview内部是继承SurfaceView和使用MediaPlayer来实现的,用的是mMediaPlayer.prepareAsync()会缓冲数据但不会阻塞,缓冲完足够数据会调用OnPrepared。

所以缓冲是必须的,你要用户界面友好点可以加个进……
[/Quote]

找到了是在frameworks\base\media\libstagefright\AwesomePlayer.cpp内定义的

void AwesomePlayer::onBufferingUpdate() {
Mutex::Autolock autoLock(mLock);
if (!mBufferingEventPending) {
return;
}
mBufferingEventPending = false;

if (mCachedSource != NULL) {
bool eos;
size_t cachedDataRemaining = mCachedSource->approxDataRemaining(&eos);

if (eos) {
notifyListener_l(MEDIA_BUFFERING_UPDATE, 100);
if (mFlags & PREPARING) {
LOGV("cache has reached EOS, prepare is done.");
finishAsyncPrepare_l();
}
} else {
int64_t bitrate;
if (getBitrate(&bitrate)) {
size_t cachedSize = mCachedSource->cachedSize();
int64_t cachedDurationUs = cachedSize * 8000000ll / bitrate;

int percentage = 100.0 * (double)cachedDurationUs / mDurationUs;
if (percentage > 100) {
percentage = 100;
}

notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage);
} else {
// We don't know the bitrate of the stream, use absolute size
// limits to maintain the cache.

const size_t kLowWaterMarkBytes = 40000;
const size_t kHighWaterMarkBytes = 200000;

if ((mFlags & PLAYING) && !eos
&& (cachedDataRemaining < kLowWaterMarkBytes)) {
LOGI("cache is running low (< %d) , pausing.",
kLowWaterMarkBytes);
mFlags |= CACHE_UNDERRUN;
pause_l();
notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START);
} else if (eos || cachedDataRemaining > kHighWaterMarkBytes) {
if (mFlags & CACHE_UNDERRUN) {
LOGI("cache has filled up (> %d), resuming.",
kHighWaterMarkBytes);
mFlags &= ~CACHE_UNDERRUN;
play_l();
notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END);
} else if (mFlags & PREPARING) {
LOGV("cache has filled up (> %d), prepare is done",
kHighWaterMarkBytes);
finishAsyncPrepare_l();
}
}
}
}
}

int64_t cachedDurationUs;
bool eos;
if (getCachedDuration_l(&cachedDurationUs, &eos)) {
if ((mFlags & PLAYING) && !eos
&& (cachedDurationUs < kLowWaterMarkUs)) {
LOGI("cache is running low (%.2f secs) , pausing.",
cachedDurationUs / 1E6);
mFlags |= CACHE_UNDERRUN;
pause_l();
notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START);
} else if (eos || cachedDurationUs > kHighWaterMarkUs) {
if (mFlags & CACHE_UNDERRUN) {
LOGI("cache has filled up (%.2f secs), resuming.",
cachedDurationUs / 1E6);
mFlags &= ~CACHE_UNDERRUN;
play_l();
notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END);
} else if (mFlags & PREPARING) {
LOGV("cache has filled up (%.2f secs), prepare is done",
cachedDurationUs / 1E6);
finishAsyncPrepare_l();
}
}
}

postBufferingEvent_l();
}





fontlose 2011-08-19
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 jayson888 的回复:]
还有个问题,如果提取出mediaplayer,单独编译,能否在源码里去掉缓冲这一部分,不知道你了不了解?

[/Quote]

源码在frameworks\base\media\libmedia我看一下先

mediaplayer的是模拟视频流播放的 要边下边播放 这里有个帖子
http://www.360doc.com/content/11/0303/11/573136_97697574.shtml
jayson888 2011-08-19
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 fontlose 的回复:]
引用 4 楼 jayson888 的回复:
引用 3 楼 fontlose 的回复:
Videoview内部是继承SurfaceView和使用MediaPlayer来实现的,用的是mMediaPlayer.prepareAsync()会缓冲数据但不会阻塞,缓冲完足够数据会调用OnPrepared。

所以缓冲是必须的,你要用户界面友好点可以加个进度条(转圈的),然后再OnPrepared内……
[/Quote]

还有个问题,如果提取出mediaplayer,单独编译,能否在源码里去掉缓冲这一部分,不知道你了不了解?
jayson888 2011-08-19
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 fontlose 的回复:]
引用 4 楼 jayson888 的回复:
引用 3 楼 fontlose 的回复:
Videoview内部是继承SurfaceView和使用MediaPlayer来实现的,用的是mMediaPlayer.prepareAsync()会缓冲数据但不会阻塞,缓冲完足够数据会调用OnPrepared。

所以缓冲是必须的,你要用户界面友好点可以加个进度条(转圈的),然后再OnPrepared内……
[/Quote]

恩,我明白了,也就是说VideoView里面其实还是mediaplayer,按你的意思是它必须要缓冲数据,才能支持比如暂停,快进这样的操作。我用videoView的RTSP做流媒体接收是看好它硬解码这一块,无需移植解码库了,但是这个缓冲5秒实在无法忍受,不能实时就不用它了,移植解码库过来吧

谢谢你的回复,再等一等就结贴了
fontlose 2011-08-19
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 jayson888 的回复:]
你的意思是关掉OnPrepare会关闭缓冲么?
[/Quote]
是关掉进度条。。
fontlose 2011-08-19
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 jayson888 的回复:]
引用 3 楼 fontlose 的回复:
Videoview内部是继承SurfaceView和使用MediaPlayer来实现的,用的是mMediaPlayer.prepareAsync()会缓冲数据但不会阻塞,缓冲完足够数据会调用OnPrepared。

所以缓冲是必须的,你要用户界面友好点可以加个进度条(转圈的),然后再OnPrepared内关掉.


谢谢你的回答,很有帮助,我看……
[/Quote]
这句是和prepare比较的 prepare是阻塞直到缓冲完函数才返回 prepareAsync是马上返回 缓冲是在后台进行 这时进入Preparing状态而不是马上进入Prepared状态。缓冲是为了看起来更流畅些要不看一下断一下也不好吧。



jayson888 2011-08-19
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 fontlose 的回复:]
Videoview内部是继承SurfaceView和使用MediaPlayer来实现的,用的是mMediaPlayer.prepareAsync()会缓冲数据但不会阻塞,缓冲完足够数据会调用OnPrepared。

所以缓冲是必须的,你要用户界面友好点可以加个进度条(转圈的),然后再OnPrepared内关掉.
[/Quote]

你的意思是关掉OnPrepare会关闭缓冲么?
jayson888 2011-08-19
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 fontlose 的回复:]
Videoview内部是继承SurfaceView和使用MediaPlayer来实现的,用的是mMediaPlayer.prepareAsync()会缓冲数据但不会阻塞,缓冲完足够数据会调用OnPrepared。

所以缓冲是必须的,你要用户界面友好点可以加个进度条(转圈的),然后再OnPrepared内关掉.
[/Quote]

谢谢你的回答,很有帮助,我看到了MediaPlayer.prepareAsync的说明:

Prepares the player for playback, asynchronously. After setting the datasource and the display surface, you need to either call prepare() or prepareAsync(). For streams, you should call prepareAsync(), which returns immediately, rather than blocking until enough data has been buffered.

显然接收的是stream,它说returns immediately rather than blocking until enough data has been buffered是不是不需要缓冲的意思啊?
fontlose 2011-08-19
  • 打赏
  • 举报
回复
Videoview内部是继承SurfaceView和使用MediaPlayer来实现的,用的是mMediaPlayer.prepareAsync()会缓冲数据但不会阻塞,缓冲完足够数据会调用OnPrepared。

所以缓冲是必须的,你要用户界面友好点可以加个进度条(转圈的),然后再OnPrepared内关掉.
jayson888 2011-08-19
  • 打赏
  • 举报
回复
结果就是VideoView能播放我发的264码流,但播放前会有5S的延时,我要处理这延时问题
儿大不由爷 2011-08-19
  • 打赏
  • 举报
回复
没结果贴啊
fontlose 2011-08-19
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 jayson888 的回复:]
引用 10 楼 fontlose 的回复:
引用 9 楼 jayson888 的回复:
还有个问题,如果提取出mediaplayer,单独编译,能否在源码里去掉缓冲这一部分,不知道你了不了解?



源码在frameworks\base\media\libmedia我看一下先

mediaplayer的是模拟视频流播放的 要边下边播放 这里有个帖子
http://www.360d……
[/Quote]
恩 这个要实际测一下了 如果底层定死必须缓冲就没法了。


1. sipdroid\src\org\zoolu 中是sip协议栈的实现 2. sipdroid\src\org\sipdroid 中是软电话的实现 3. sipdroid\src\com 中是stun相关的实现 4. sipdroid默认使用的编码格式为G711-A率。 5. 直接用ant debug的方法编译出的程序,只支持A率和U率两种音频编码格式,其他的都需要通过NDK的方法导入后,才能使用。 6. 如果对端终端支持视频的话(如linphone),菜单如下: 保持,静音, 转移 发送视频 挂断 注意:只能发送视频,接收不到对端的视频。 7. 如果对端终端不支持视频的话(如yate),菜单如下: 保持,静音, 转移 挂断 8. sipdroid\src\org\sipdroid\sipua\ui 中的VideoCamera.java,有视频捕获,发送,接收的实现。 9. sipdroid\src\org\sipdroid\sipua\ui 中的CallScreen.java中的 VIDEO_MENU_ITEM 标识了 “发送视频” 10. Activity2.java 实现了跳转到InCallScreen.java 11. class InCallScreenextends CallScreen 12. sipdroid.java 中有“关于 退出 设置”菜单的实现。 在AndroidManifest.xml中, 表明了哪个Activity先启动。 13. 网络传来的音频数据通过AudioTrack类进行播放。 14. 本地的音频数据通过AudioRecord类进行录制。 15. 在本地播放数据包中的视频流,可以先提取位图,再显示。由于系统没有提供直接播放的相关方法。 16. 线程同步的方法 – synchronized 17. F:\sipdroid\res\drawable 中的图标可以更换 18. sipdroid\res\values-zh-rCN 修改【关于】显示框的内容 19 在Sipdroid开源项目像服务器进行数据的发送统一是由SipProvider的sendMessage,因为首先得知道是什么连接 是UDP啊,还是TCP,然后就是message的封装 20. 是无连接的包投递服务,为什么是无连接呢,客户端和服务器压根就没有建立连接,服务器只是开放了端口来接受数据,有了就接受,没有就悬挂阻塞. 21双边的视频观看,走的还是数据报包,有数据报包的ip和端口就行了 22 但是Sipdroid可以直接的从MediaRecord里面已经生成好的视频数据中提取出H264/H263的数据,这些数据已经经过了相应的编码 23如何观看视频: mVideoFrame.setVideoURI(Uri.parse("rtsp://"+Receiver.engine(mContext).getRemoteAddr()+"/"+ Receiver.engine(mContext).getRemoteVideo()+"/sipdroid")); 24 通过内置的videoview来通过RTSP来进行播放,那么也就是说服务器会将传递的RTP的视频数据流封装成RTSP的流传递给手机的videoview来实现观看,同样也不需要解码库, 所以Sipdroid开源代码里只有声音的编码库,没有视频的编码库. 25 最好的实现该软件的方法是,借助Android的MediaRecorder实时提取出H263/H264数据,然后经过RTP封装传给RTSP服务器,这种实现方式最理想,通过获取onPrewFrame来获取预览帧编码,无论怎么弄,不可避免的,延时,丢帧各种情况都会让你非常的棘手

80,351

社区成员

发帖
与我相关
我的任务
社区描述
移动平台 Android
androidandroid-studioandroidx 技术论坛(原bbs)
社区管理员
  • Android
  • yechaoa
  • 失落夏天
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧