本文转自EasyDarwin团队John的博客:http://blog.csdn.net/jyt0551,John是EasyPusher安卓直播推流、EasyPlayer直播流媒体播放端的开发和维护者,在这方面为开源社区贡献了非常多的技术干货和代码,这里对John的辛苦劳作表示感谢!

在之前一片博客《 EasyPusher实现安卓Android手机直播推送同步录像功能》(http://blog.csdn.net/jyt0551/article/details/58714595)中,我写到了EasyPusher推送的同时进行本地存储的功能,我们今天来介绍下EasyPlayer保存本地录像的功能。EasyPlayer同样是运用MediaMuxer进行录像的,与EasyPusher不同的是,Player要保存的是远端的音视频码流。目前Player支持对H264格式的视频和AAC格式的音频进行存储。

在前一篇博客 ,音视频码流的metadata,即MediaFormat,是从MediaCodec取出来的。也就是说硬编码库提供了获取音视频的metadata的接口。但是很可惜我们在播放端并没有这样方便的借口可以调用。那MediaFormat对象只能我们手动构建了。

MediaFormat这个类的实现非常简单,它的内部以键值对的形式对音视频的参数进行了封装,并且向外提供了接口以供读写。因而我们可以创建一个MediaFormat对象,并使用特定的参数对其赋值即可。经作者研究发现,在录像时,对于视频流,需要的metadata如下表所示。

数据 说明
KEY_MIME 视频的MIME,比如video/avc
width 宽度
height 高度
csd-0 SPS
csd-1 PPS

对于音频,需要如下信息:

数据 说明
KEY_MIME 音频的MIME,比如audio/mp4a-latm
KEY_CHANNEL_COUNT 通道数
KEY_SAMPLE_RATE 采样率
csd-0 一些更多的细节信息,比如profile、sample的索引等。参考exoplayer里的音频数据的处理
csd-1 这个。。更多的细节,就不太清楚了。作者也是参考了exoplayer里面的处理

了解了这些基本信息后,接下来我们要做的就是从码流中获取到这些信息,并构建MediaFormat,用来添加Video或Audio Track.

下面是获取到视频相关信息后,添加一个VideoTrack的代码。

// 添加Video Track
MediaFormat format = new MediaFormat();
format.setInteger(MediaFormat.KEY_WIDTH, mWidth);
format.setInteger(MediaFormat.KEY_HEIGHT, mHeight);
mCSD0.clear();
format.setByteBuffer("csd-0", mCSD0);
mCSD1.clear();
format.setByteBuffer("csd-1", mCSD1);
format.setString(MediaFormat.KEY_MIME, "video/avc"); Log.i(TAG, String.format("addTrack video track :%s", format));
mMuxerVideoTrack = muxer.addTrack(format);

下面是添加AudioTrack的代码。

int audioObjectType = 2;
byte[] audioSpecificConfig = CodecSpecificDataUtil.buildAacAudioSpecificConfig(audioObjectType, sampleRateIndex, channelConfig);
Pair<Integer, Integer> audioParams = CodecSpecificDataUtil.parseAacAudioSpecificConfig(audioSpecificConfig);
// format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 0);
format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_AUDIO_AAC);
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, audioParams.second);
format.setInteger(MediaFormat.KEY_SAMPLE_RATE, audioParams.first); List<byte[]> bytes = Collections.singletonList(audioSpecificConfig);
for (int j = 0; j < bytes.size(); j++) {
format.setByteBuffer("csd-" + j, ByteBuffer.wrap(bytes.get(j)));
} Log.i(TAG, String.format("addTrack audio track :%s", format));
mMuxerAudioTrack = muxer.addTrack(format);

至此,音视频的通道都已经添加完成,接下来就是要写数据了。代码如下:

private synchronized void pumpSample(RTSPClient.FrameInfo frameInfo) {
if (mObject == null) return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
MediaMuxer muxer = (MediaMuxer) mObject;
MediaCodec.BufferInfo bi = new MediaCodec.BufferInfo();
bi.offset = frameInfo.offset;
bi.size = frameInfo.length;
ByteBuffer buffer = ByteBuffer.wrap(frameInfo.buffer, bi.offset, bi.size);
bi.presentationTimeUs = frameInfo.stamp;
try {
if (frameInfo.audio) {
bi.offset += 7;
bi.size -= 7;
if (mMuxerAudioTrack > -1)
muxer.writeSampleData(mMuxerAudioTrack, buffer, bi);
} else if (mMuxerVideoTrack > -1) {
if (frameInfo.type != 1) {
bi.flags = 0;
} else {
bi.flags = MediaCodec.BUFFER_FLAG_KEY_FRAME;
}
muxer.writeSampleData(mMuxerVideoTrack, buffer, bi);
}
} catch (IllegalStateException ex) {
ex.printStackTrace();
} catch (IllegalArgumentException ex) {
ex.printStackTrace();
}
}
}

最后,在录像完成后,关闭MediaMuxer,释放相关资源。


public synchronized void stopRecord() {
mMuxerAudioTrack = mMuxerVideoTrack = -1;
mRecordingPath = null;
if (mObject == null) return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
MediaMuxer muxer = (MediaMuxer) mObject;
try {
muxer.release();
} catch (IllegalStateException ex) {
ex.printStackTrace();
} catch (IllegalArgumentException ex) {
ex.printStackTrace();
}
}
mObject = null; ResultReceiver rr = mRR;
if (rr != null) {
rr.send(RESULT_RECORD_END, null);
}
}

获取更多信息

邮件:support@easydarwin.org

WEB:www.EasyDarwin.org

Copyright © EasyDarwin.org 2012-2017

EasyPlayer Android安卓流媒体播放器实现播放同步录像功能实现(附源码)的更多相关文章

  1. Android 仿 新闻阅读器 菜单弹出效果(附源码DEMO)

    这一系列博文都是:(android高仿系列)今日头条 --新闻阅读器 (一) 开发中碰到问题之后实现的,觉得可能有的开发者用的到或则希望独立成一个小功能DEMO,所以就放出来这么一个DEMO. 原本觉 ...

  2. Android 音视频深入 四 录视频MP4(附源码下载)

    本篇项目地址,名字是<录音视频(有的播放器不能放,而且没有时长显示)>,求star https://github.com/979451341/Audio-and-video-learnin ...

  3. 【Android初级】如何动态添加菜单项(附源码+避坑)

    我们平时在开发过程中,为了灵活多变,除了使用静态的菜单,还有动态添加菜单的需求.今天要分享的功能如下: 在界面的右上角有个更多选项,点开后,有两个子菜单:关于和退出 点击"关于", ...

  4. Android 音视频深入 五 完美的录视频(附源码下载)

    本篇项目地址,名字是录视频,求star https://github.com/979451341/Audio-and-video-learning-materials 这一次的代码录视频在各个播放器都 ...

  5. Android 音视频深入 八 小视频录制(附源码下载)

    本篇项目地址,求starthttps://github.com/979451341/Audio-and-video-learning-materials/tree/master/%E5%B0%8F%E ...

  6. Google Spreadsheet Add-on Links Extractor 谷歌表格插件链接提取器的制作与发布(附源码)

    引言 为什么想到制作这么一个插件呢,是因为博主在更新微信公众号[刷尽天下]的后台数据库时,需要有博客园题目帖子的链接,那么就要从这篇帖子 LeetCode All in One 题目讲解汇总(持续更新 ...

  7. [转]Android事件分发机制完全解析,带你从源码的角度彻底理解(上)

    Android事件分发机制 该篇文章出处:http://blog.csdn.net/guolin_blog/article/details/9097463 其实我一直准备写一篇关于Android事件分 ...

  8. Android 高仿 频道管理----网易、今日头条、腾讯视频 (可以拖动的GridView)附源码DEMO

    距离上次发布(android高仿系列)今日头条 --新闻阅读器 (二) 相关的内容已经半个月了,最近利用空闲时间,把今日头条客户端完善了下.完善的功能一个一个全部实现后,就放整个源码.开发的进度就是按 ...

  9. 死磕 java同步系列之ReentrantReadWriteLock源码解析

    问题 (1)读写锁是什么? (2)读写锁具有哪些特性? (3)ReentrantReadWriteLock是怎么实现读写锁的? (4)如何使用ReentrantReadWriteLock实现高效安全的 ...

随机推荐

  1. maven打包加入依赖包以及加入本地依赖包的方法

    Maven引入本地Jar包并打包进War包中 1.概述 在平时的开发中,有一些Jar包因为种种原因,在Maven的中央仓库中没有收录,所以就要使用本地引入的方式加入进来. 2. 拷贝至项目根目录 项目 ...

  2. Manjaro中源码安装gcc7.1

    刚刚gcc 7.1也出来了,想在使用熟悉的linux下试试,特记录如下: 准备必要的系统环境:(升级系统到最新,安装必要的工具) pacman -Syyu                        ...

  3. Django 博客首页视图

    Django 处理 HTTP 请求 Web 应用的交互过程其实就是 HTTP 请求与响应的过程.无论是在 PC 端还是移动端,我们通常使用浏览器来上网,上网流程大致来说是这样的: 我们打开浏览器,在地 ...

  4. hdu 1099(数学)

    Lottery Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Sub ...

  5. 容器中元素的去重——ans.erase(unique(ans.begin(),ans.end()),ans.end());

    啊,原来unique()函数,只是处理的连续的元素,比如 -1 -1 -1 1 2 -1 2 就处理成了 -1 1 2 -1 2 -1 -1并且返回5,之后eraser(5,7)剩下了 -1 1 2 ...

  6. PHP实现自定义中奖和概率算法

    最近玩<QQ飞车手游>,出了一款点券A车,需要消耗抽奖券抽奖,甚是激动,于是抽了几次,没想到中的都是垃圾道具,可恨可叹~~ 这几天项目中也涉及到了类似的概率操作,于是思考了一下,简单分装了 ...

  7. Codeforces 932 B.Recursive Queries-前缀和 (ICM Technex 2018 and Codeforces Round #463 (Div. 1 + Div. 2, combined))

    B. Recursive Queries   time limit per test 2 seconds memory limit per test 256 megabytes input stand ...

  8. Codeforces 835F Roads in the Kingdom (环套树 + DP)

    题目链接 Roads in the Kingdom 题意  给出一个环套树的结构,现在要删去这个结构中的一条边,满足所有点依然连通. 删边之后的这个结构是一棵树,求所有删边情况中树的直径的最小值. 显 ...

  9. 计蒜客 微软大楼设计方案(RMQ)

    题目链接 微软大楼设计方案 中文题就不说题意了~ 首先是简单版本 满足$1 <= n, m <= 50$ 那么设$c[i][j]$为从第$i$幢楼到第$j$幢楼的最低的那幢楼的高度 计算两 ...

  10. 洛谷——P2658 汽车拉力比赛

    P2658 汽车拉力比赛 题目描述 博艾市将要举行一场汽车拉力比赛. 赛场凹凸不平,所以被描述为M*N的网格来表示海拔高度(1≤ M,N ≤500),每个单元格的海拔范围在0到10^9之间. 其中一些 ...