最近的项目中遇到一个非常头痛的需求,在android端录制视频的时候动态添加像监控画面一样的精确到秒的时间信息,关键是,并不是说只在播放器的界面显示时间就可以了,而是录制到视频里面去,这个MP4在电脑上播放也能看到每个画面的时间。
最后想到的办法是在录制完成以后去处理这个视频。

期间参考了很多资料,比较有用的大概是ffmpeg和比较新的Api
mediaCodec系列了。介于ffmpeg都是C实现,和一大堆NDK相关,本人不是太懂,就重点关注了MediaCodec系列。

参考逻辑流程图一目了然的这篇博文
http://blog.csdn.net/xipiaoyouzi/article/details/37599759

MediaCodec进行编解码的大体逻辑是这样的(转载):

主要函数的调用逻辑如下:

MediaExtractor,MediaCodec,MediaMuxer这三个Api已经可以很多多媒体处理工作了,比如用MediaExtractor+MediaMuxer就可以做音视频剪辑,MediaCodec+MediaMuxer就可以做自定义的录像机,一起用就可以做特效编辑,滤镜之类的了。

添加时间水印效果

关键在于取到的数据帧,是YUV格式的,根据拍摄时选取的不同还不一样,我用到的NV21格式,也就是YUV420sp,拿到NV21格式的帧以后,转成RGB渲染,然后又转回NV21交给encoder,看起来好笨重,也非常地耗时,但我还没找到更好的办法。

    private Bitmap first;private void handleFrameData(byte[] data, MediaCodec.BufferInfo info) {//YUV420sp转RGB数据 5-60msByteArrayOutputStream out = new ByteArrayOutputStream();YuvImage yuvImage = new YuvImage(data, ImageFormat.NV21, srcWidth, srcHeight, null);yuvImage.compressToJpeg(new Rect(0, 0, srcWidth, srcHeight), 100, out);byte[] imageBytes = out.toByteArray();//旋转图像,顺便解决电脑上播放被旋转90度的问题 20-50msBitmap image = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);Bitmap bitmap = rotaingImageView(videoRotation, image);image.recycle();//渲染文字 0-1msCanvas canvas = new Canvas(bitmap);canvas.drawText(videoTimeFormat.format(videoFirstTime + info.presentationTimeUs / 1000), 10, 30, paint);//预览处理帧 0-5msfirst = bitmap;handler.sendEmptyMessage((int) (info.presentationTimeUs / 1000));synchronized (MediaCodec.class) {//记得加锁timeDataContainer.add(new Frame(info, bitmap));}}/** 旋转图片* @param angle* @param bitmap* @return Bitmap*/public Bitmap rotaingImageView(int angle, Bitmap bitmap) {//旋转图片 动作Matrix matrix = new Matrix();matrix.postRotate(angle);// 创建新的图片return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);}

然后是转回NV21

/*** 获取夹了时间戳的的数据** @return*/private Frame getFrameData() {synchronized (MediaCodec.class) {//记得加锁if (timeDataContainer.isEmpty()) {return null;}//从队列中获取数据Frame frame = timeDataContainer.remove(0);取出后将此数据remove掉 既能保证PCM数据块的取出顺序 又能及时释放内存//转回YUV420sp 120-160msframe.data = getNV21(dstWidth, dstHeight, frame.bitmap);return frame;}}public static byte[] getNV21(int width, int height, Bitmap scaled) {int[] argb = new int[width * height];scaled.getPixels(argb, 0, width, 0, 0, width, height);byte[] yuv = new byte[width * height * 3 / 2];encodeYUV420SP(yuv, argb, width, height);scaled.recycle();return yuv;}/*** 将bitmap里得到的argb数据转成yuv420sp格式* 这个yuv420sp数据就可以直接传给MediaCodec,通过AvcEncoder间接进行编码** @param yuv420sp 用来存放yuv420sp数据* @param argb     传入argb数据* @param width    图片width* @param height   图片height*/public static void encodeYUV420SP(byte[] yuv420sp, int[] argb, int width, int height) {final int frameSize = width * height;int yIndex = 0;int uvIndex = frameSize;int a, R, G, B, Y, U, V;int index = 0;for (int j = 0; j < height; j++) {for (int i = 0; i < width; i++) {//                a = (argb[index] & 0xff000000) >> 24; // a is not used obviouslyR = (argb[index] & 0xff0000) >> 16;G = (argb[index] & 0xff00) >> 8;B = (argb[index] & 0xff) >> 0;// well known RGB to YUV algorithmY = ((66 * R + 129 * G + 25 * B + 128) >> 8) + 16;U = ((-38 * R - 74 * G + 112 * B + 128) >> 8) + 128;V = ((112 * R - 94 * G - 18 * B + 128) >> 8) + 128;// NV21 has a plane of Y and interleaved planes of VU each sampled by a factor of 2//    meaning for every 4 Y pixels there are 1 V and 1 U.  Note the sampling is every other//    pixel AND every other scanline.yuv420sp[yIndex++] = (byte) ((Y < 0) ? 0 : ((Y > 255) ? 255 : Y));if (j % 2 == 0 && index % 2 == 0) {yuv420sp[uvIndex++] = (byte) ((V < 0) ? 0 : ((V > 255) ? 255 : V));yuv420sp[uvIndex++] = (byte) ((U < 0) ? 0 : ((U > 255) ? 255 : U));}index++;}}}

看到上面的代码执行耗时,根本不可能实时录制时处理,就算后台服务处理,3秒钟的720*480视频得花费约20秒..但把encodeYUV420SP等换成JNI实现后,速度加快了很多。

初始化编码器,设置编码后的视频格式

    /*** 初始化编码器*/private void initMediaEncode() {try {MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, dstWidth, dstHeight);format.setInteger(MediaFormat.KEY_BIT_RATE, 1024 * 512);format.setInteger(MediaFormat.KEY_FRAME_RATE, 27);format.setInteger(MediaFormat.KEY_COLOR_FORMAT, getVideoColorFormat());format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);mediaEncode = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);mediaEncode.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);} catch (IOException e) {e.printStackTrace();}mediaEncode.start();}/*** 获取颜色格式*/private int getVideoColorFormat() {String model = android.os.Build.MODEL;JLog.d("px", "phone model string is " + model);if (model.startsWith("MI")) {//小米return MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar;} else if (videoColorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar) {return videoColorFormat;} else {return MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar;}}

在Service中管理这任务,例如

 @Overridepublic int onStartCommand(Intent intent, int flags, int startId) {super.onStartCommand(intent, flags, startId);int action = intent.getIntExtra("action", 0);if (action == REQUEST_CODEC) {VideoCodecModel video = (VideoCodecModel) intent.getSerializableExtra("video");video = codecDao.addItem(video);if (mVideo == null) {//空闲start(video);} else {//排队videos.add(video);}}return START_NOT_STICKY;}private void start(VideoCodecModel video) {mTask = new VideoCodecTask(video);mTask.setProgressHandler(handler);mTask.start();}

打开Acitivity时,绑定服务,可以查看服务的进行状态

@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_show_codec);VideoCodecDao codecDao = VideoCodecDao.getInstance(this);final Intent intent = new Intent(this, WaterMarkService.class);connection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {Log.d("px", "onServiceConnected");//这里利用一个回调方法去监听服务的运行状态.binder.setOnProgressChangeListener(ShowCodecActivity.this);}@Overridepublic void onServiceDisconnected(ComponentName name) {}};bindService(intent, connection, Context.BIND_AUTO_CREATE);}@Overridepublic void onProgress(int progress, int max) {}@Overridepublic void onCodecStart(VideoCodecModel video) {}@Overridepublic void onCodecFinish(VideoCodecModel video) {}@Overridepublic void onCodecError(VideoCodecModel video, String msg) {}@Overridepublic void onCodecCancel(VideoCodecModel video, boolean delete) {}

代码片段

简单deomo的git地址

android视频处理之动态时间水印效果相关推荐

  1. 如何用html制作一个动态烟花,视频加烟花特效 视频如何制作烟花效果|视频上添加动态的焰火效果...

    有句mmp不知当讲不当讲,今天竟然是孔子的诞辰,这样一算孔子很有可能是处女座呢!!当看到这条消息时我的内心是拒绝的,在我看来孔子的中庸思想表示的"平庸.折中.调和"明明是我们天秤座 ...

  2. 使用html5制作烟花的视频,视频加烟花特效 视频如何制作烟花效果|视频上添加动态的焰火效果...

    有句mmp不知当讲不当讲,今天竟然是孔子的诞辰,这样一算孔子很有可能是处女座呢!!当看到这条消息时我的内心是拒绝的,在我看来孔子的中庸思想表示的"平庸.折中.调和"明明是我们天秤座 ...

  3. Android开发之实现动态打勾效果(DrawHookView)

    android开发之实现动态打勾效果(DrawHookView) 转载于:https://www.cnblogs.com/zhujiabin/p/7498161.html

  4. android ndk之opencv+MediaCodec硬编解码来处理视频动态时间水印

    android ndk之opencv+MediaCodec硬编解码来处理视频水印学习笔记 android视频处理学习笔记.以前android增加时间水印的需求,希望多了解视频编解码,直播,特效这一块, ...

  5. html5火苗特效代码,视频添加火焰特效 视频画面中加动态的火苗效果

    我想好了等我三十岁的时候还没有结婚的话,我就给那些结了婚.生了孩子.我随了份子钱的人打电话,让他们来参加我的三十岁大寿,我想我这样做他们应该可以理解吧!!好了来看看我们今天的教程吧,同样也是视频加特效 ...

  6. android 按键点击触摸有水印效果_“100例”—优秀产品设计按键细节设计美图

    品索设计,让设计考研 \ 就业 \ 留学更简单欢迎关注「品索设计」按键是产品设计中非常重要的一个细节,它是一个产品中与人零距离接触次数最多的一个地方.设计可不是单单有创意就可以的,要想把创意展现得好, ...

  7. android仿朋友圈教程,android 仿朋友圈动态 图片查看效果

    [实例简介] [实例截图] [核心代码] package com.example.imagedemo; import java.util.ArrayList; import android.os.Bu ...

  8. ffmpeg实现视频实时动态时间水印

    首先看下效果 左上角的实时当前时间,纯c完成.下面的是hello ffmpeg字样是avfilter的drawtext实现. 动态时间水印,其实ffmpeg有命令可以实现,不过我在windows下发现 ...

  9. html位置插入透明动画文字,视频加移动水印 视频添加图片加文字水印 设置透明漂浮移动并控制显示时间...

    有没有小伙伴平时在看一些视频的时候,视频里会有一张图片然而图片里面有文字,然后是透明的图片,并且还漂浮移动在视频画面里,过了一会儿就自动消失了.这也是一直添加水印的方法,不过是把图片设置了半透明的样子 ...

最新文章

  1. OCP 12c最新考试原题及答案(071-3)
  2. 遵义春季招生计算机学校,遵义计算机学校招生
  3. java广度优先遍历
  4. MVC View 中 html 属性名与关键字冲突问题的分析与解决
  5. Numpy数据分析数值范围调整、计算
  6. 发布9个月直降2300!这款手机太惨了:卖完下架
  7. minus出错matlab,请求帮忙指点MATLAB中的语法错误
  8. 杂项:HTML5-3/3-技术要点
  9. 如何开具和交付给客户电子发票
  10. Ubuntu下解压缩文件
  11. app测试用mysql数据库,数据库: 安装配置数据库,使用Navicat for MySQL和手机APP 连接测试...
  12. linux 中文 文件名乱码,中文文件名乱码问题
  13. 【超图+CESIUM】【基础API使用示例】34、超图|CESIUM - 绘制光流流光线段
  14. 在线URL解码还原工具
  15. Python 与金融科技2|数据的滑动平均值及多图可视化
  16. KinectV2 qt opencv 实现平面测量
  17. 高考志愿填报APP靠谱吗?大数据仅供参考不能依赖
  18. synology nfs_如何手动和自动关闭和重新启动Synology NAS
  19. HTML——多媒体语义化标签
  20. c语言 在有n个元素的数组s中查找书名为a的书,问题:单选(2分) 已定义float a[5];,则数组a可引用的元素有( )。...

热门文章

  1. Hanselman的精彩事事通讯:2014年1月21日
  2. 将print打印的内容保存到日志
  3. 【数据结构 严蔚敏版】 二叉树 基本操作
  4. 极路由3HC5861刷openwrt
  5. PAN++ 端到端场景文本识别【识别部分精讲】
  6. 开封大学计算机专业录取分数线,开封大学录取分数线2021是多少分(附历年录取分数线)...
  7. Joining_thread简单实现
  8. linux 查看用户权限组,linux用户组及权限
  9. maven打包报错 Failed to execute goal org.apache.maven.plugins:maven-jar-plugin:3.0.2:jar
  10. python字典修改键所对应值_详解如何修改python中字典的键和值