由于需要在高通平台上做行车记录的功能,需要给视频加上时间水印。

网上搜索了一下方案,发现有在MTK平台做过类似功能的,参考:
http://blog.csdn.net/jimbo_lee/article/details/27545193

最直接的想法就是获取到视频编码前每一帧YUV数据,将时间水印覆盖到对应的像素点上,然后再进行视频编码。

效果图:

主要实现过程:
1.准备0-9数字以及- / 等符号(时间水印中包含的元素)的Bitmap图片,如果是灰度的时间水印,8位深度的Bitmap就可以。Bitmap图片转换成对应的字节数组可以借助工具实现,当然也可以自己解析Bitmap文件(参考http://blog.csdn.net/smileorcryps/article/details/60756967),获取到时间对应的字节数组后,直接覆盖YUV中相应位置的Y分量就可以了;

2.找到相机中video数据流后处理的位置,高通平台hardware/qcom/camera/QCamera2/HAL/QCamera2HWICallbacks.cpp(具体的位置参考后面贴的patch),在这里先根据当前时间获取到具体的水印字节数组,然后修改YUV数据中Y分量(NV21格式,Y单独存储,修改起来也相对简单),这样最后生成的视频中就会包含时间水印了。

问题:时间水印闪烁跳动

之前是修改的frameworks中CameraSource.cpp

void CameraSource::dataCallbackTimestamp(int64_t timestampUs,
int32_t msgType, const sp &data) {

这种方式会造成水印不规则闪烁,怀疑是内存缓存的问题,函数是有同步控制的,for循环中的值直接跑乱了。

后来换成在hardware的video流处理阶段进行处理就可以了。

只针对720p视频修改的patch:

diff --git a/hardware/qcom/camera/QCamera2/HAL/QCamera2HWI.cpp b/hardware/qcom/camera/QCamera2/HAL/QCamera2HWI.cpp
old mode 100644
new mode 100755
index 60f7191..891d743
--- a/hardware/qcom/camera/QCamera2/HAL/QCamera2HWI.cpp
+++ b/hardware/qcom/camera/QCamera2/HAL/QCamera2HWI.cpp
@@ -1022,6 +1022,7 @@ QCamera2HardwareInterface::QCamera2HardwareInterface(uint32_t cameraId)mAdvancedCaptureConfigured(false),mFPSReconfigure(false){
+   mWatermarkBuf = NULL;#ifdef TARGET_TS_MAKEUPmMakeUpBuf = NULL;memset(&mFaceRect, -1, sizeof(mFaceRect));
@@ -2222,6 +2223,13 @@ int QCamera2HardwareInterface::startRecording()if (rc == NO_ERROR) {rc = startChannel(QCAMERA_CH_TYPE_VIDEO);}
+
+   if (mWatermarkBuf == NULL) {+        int vid_width, vid_height;
+        mParameters.getVideoSize(&vid_width, &vid_height);
+        mWatermarkBuf = new unsigned char[vid_width*vid_height*3/2];
+        CDBG_HIGH("vid_width=%d,vid_height=%d",vid_width, vid_height);
+    }#ifdef HAS_MULTIMEDIA_HINTSif (rc == NO_ERROR) {
@@ -2259,6 +2267,11 @@ int QCamera2HardwareInterface::stopRecording()}}#endif
+
+   if (mWatermarkBuf) {+        delete []mWatermarkBuf;
+        mWatermarkBuf=NULL;
+    }CDBG_HIGH("%s: X", __func__);return rc;}
diff --git a/hardware/qcom/camera/QCamera2/HAL/QCamera2HWI.h b/hardware/qcom/camera/QCamera2/HAL/QCamera2HWI.h
old mode 100644
new mode 100755
index 236908e..d7b0f92
--- a/hardware/qcom/camera/QCamera2/HAL/QCamera2HWI.h
+++ b/hardware/qcom/camera/QCamera2/HAL/QCamera2HWI.h
@@ -656,6 +656,11 @@ private:int32_t mNumPreviewFaces;bool mAdvancedCaptureConfigured;bool mFPSReconfigure;
+
+   unsigned char *mWatermarkBuf;
+   bool WatermarkProcess_Video(mm_camera_buf_def_t *pFrame,QCameraStream * pStream);
+   bool WatermarkProcess(mm_camera_buf_def_t *pFrame,QCameraStream * pStream,unsigned char *pOutBuf);
+       //ts add for makeup#ifdef TARGET_TS_MAKEUPTSRect mFaceRect;
diff --git a/hardware/qcom/camera/QCamera2/HAL/QCamera2HWICallbacks.cpp b/hardware/qcom/camera/QCamera2/HAL/QCamera2HWICallbacks.cpp
old mode 100644
new mode 100755
index 9065e33..5bb2abc
--- a/hardware/qcom/camera/QCamera2/HAL/QCamera2HWICallbacks.cpp
+++ b/hardware/qcom/camera/QCamera2/HAL/QCamera2HWICallbacks.cpp
@@ -37,6 +37,7 @@#include <utils/Trace.h>#include <utils/Timers.h>#include "QCamera2HWI.h"
+#include "digital_data.h"namespace qcamera {@@ -552,6 +553,87 @@ bool QCamera2HardwareInterface::TsMakeupProcess(mm_camera_buf_def_t *pFrame,return bRet;}#endif
+
+bool QCamera2HardwareInterface::WatermarkProcess_Video(mm_camera_buf_def_t *pFrame,
+        QCameraStream * pStream) {+    CDBG("%s begin",__func__);
+    bool bRet = false;
+    if (pStream == NULL || pFrame == NULL) {+        bRet = false;
+        CDBG_HIGH("%s pStream == NULL || pFrame == NULL",__func__);
+    } else {+        bRet = WatermarkProcess(pFrame,pStream,mWatermarkBuf);
+    }
+    CDBG("%s end bRet = %d ",__func__,bRet);
+    return bRet;
+}
+
+bool QCamera2HardwareInterface::WatermarkProcess(mm_camera_buf_def_t *pFrame,
+        QCameraStream * pStream,unsigned char *pOutBuf) {+    bool bRet = false;
+    CDBG("%s begin",__func__);
+   if (pStream == NULL || pFrame == NULL || pOutBuf == NULL) {+        bRet = false;
+        CDBG_HIGH("%s pStream == NULL || pFrame == NULL || pMakeupOutBuf == NULL",__func__);
+    }
+
+   cam_dimension_t dim;
+    pStream->getFrameDimension(dim);
+
+   int width = dim.width;
+    int height = dim.height;
+    unsigned char *_ptr;
+   _ptr = (unsigned char*)pFrame->buffer;
+
+    if (NULL != _ptr) {
+        char dateTime[] = "2016-12-22 09:00:00";
+        time_t timer;
+        struct tm* t_tm;
+
+        time(&timer);
+        t_tm = localtime(&timer);
+        memset(dateTime, 0, sizeof(dateTime));
+        sprintf(dateTime,"%4d-%02d-%02d %02d:%02d:%02d", t_tm->tm_year+1900, t_tm->tm_mon+1, t_tm->tm_mday, t_tm->tm_hour, t_tm->tm_min, t_tm->tm_sec);
+
+        //ALOGE("==========  timestampUs:%d, systemTime():%d, dateTime:%s",timestampUs, systemTime()/1000, dateTime);
+        int digitalNums[10+8+1+ 1] = {-1};  // 10:-      11 ::      12:blank
+        memset(digitalNums, -1, sizeof(digitalNums));
+        for(int i = 0; i<19; i++){
+            char num = dateTime[i];
+            if(('0' <= num) && (num <= '9'))
+            {
+                digitalNums[i] = num - '0';
+            }else if(num == '-'){
+                digitalNums[i] = 10;
+            }else if(num == ':'){
+                digitalNums[i] = 11;
+            }else if(num == ' '){
+                digitalNums[i] = 12;
+            }
+        }
+
+       //for 1280 x 720
+       int left_offset = 776;  // width - digital_width * srtlen(dateTime) - digital_width * 2
+       int top_offset = 620;   // height - 100
+        for(int j = 0; j<digital_height; j++) {
+            for(volatile int k=0; k<19; k++) {
+                const unsigned char* str = (digitalNums[k] < 12 && digitalNums[k] != -1) ? DigitalArray[digitalNums[k]] : NULL;
+                if(str != NULL) {
+                    for(volatile int h=0; h<digital_width; h++) {
+                        if(*(str+(digital_height - 1 - j)*digital_width + h) != 0x00) {
+                            *(_ptr+top_offset*width + left_offset + j*width + k*(gap_width+digital_width) + h) = *(str+(digital_height - 1 - j)*digital_width + h);
+                        }
+                    }
+                }
+            }
+        }
+
+    }
+
+    CDBG("%s end bRet = %d ",__func__,bRet);
+    return bRet;
+}
+/*===========================================================================* FUNCTION   : postproc_channel_cb_routine*
@@ -1119,6 +1201,9 @@ void QCamera2HardwareInterface::video_stream_cb_routine(mm_camera_super_buf_t *sreturn;}mm_camera_buf_def_t *frame = super_frame->bufs[0];
+
+   //ysm
+   pme->WatermarkProcess_Video(frame,stream);if (pme->needDebugFps()) {pme->debugShowVideoFPS();

代码链接:
http://download.csdn.net/detail/smileorcryps/9773313

Android视频添加时间水印相关推荐

  1. ffmpeg给视频添加时间水印

    ffmpeg给视频添加时间水印 通过 drawtext 滤镜模块给视频添加时间水印 给视频添加时间水印 用来做片源调试,非常方便的查找和定位处理的哪一帧视频片源: 1. 添加本地时间水印 ffmpeg ...

  2. 【Qt+FFmpeg】给视频添加时间水印

    ffmpeg编解码中,给本地视频加上时间水印,并保存到本地,使用到的技术是ffmpeg中的avfilter库: 具体效果如下 yuv: mp4 本方法不适合摄像头解码,解码出来糊得不行,本地视频的话会 ...

  3. Android 拍照添加时间水印

    效果如下图 : 1.拍照 // 非空判断 拍照if (mCamera0 != null){mCamera0.takePicture(null, null, jpeg0); // 1 front} 2. ...

  4. Android 录制视频添加时间水印,不吃透都对不起自己

    mStopVideoRecording = true; Log.d(TAG, "stopVideoRecordingAsync() mMediaRecorderRecording=" ...

  5. Android录像时添加时间水印

    在网上搜索整理了下,有三篇有用的文章,原理也是一样. 实现步骤说明在这里Android 录制视频添加时间水印 上面也仅给出了步骤,具体代码在增加录像时间戳水印. camera框架介绍 可以找到.从博客 ...

  6. android利用EpMedia给录像添加时间水印

    做出来的例子效果如下: 第一步:集成EpMedia, 步骤在大神的github上都有,地址如下: https://github.com/yangjie10930/EpMedia 添加时间水印,我的方法 ...

  7. Android 录像添加时间戳水印

    最近项目中需要后台录像并添加时间戳,就类似监控视频,直接放效果图了, demo界面功能如图:跑的时候注意自己到设置加相机权限 这个demo主要做到了两点,一.添加时间戳水印.二.暂停,继续录像.git ...

  8. PHP实现给视频加图片水印,怎么在视频画面上加图片?如何给视频添加自己的图片作为水印?视频添加图片水印的方法...

    今天就是周一啦,昨天周末大家有没有跟好友去玩呢~反正小编是跟同学聚会去了,聚会主题:胖.哈哈哈,不过小编可不跟他们一样只会长胖,小编可是瘦瘦瘦的呢,偷偷的告诉你们,小编减肥瘦了三十斤哦,嘻嘻.好啦,废 ...

  9. Moviepy模块之视频添加图片水印

    文章目录 前言 视频添加图片水印 1.引入库 2.加载视频文件 3.加载水印图片 4.缩放水印图片大小 5.设置水印的位置 5.1 相对于视频的左上角 5.2 相对于视频的左下角 5.3 相对于视频的 ...

最新文章

  1. golang go-sql-driver 数据库报错 bad connection
  2. UA MATH564 概率论IV 次序统计量例题2
  3. python 将excel文件转换为txt文件_python利用pandas将excel文件转换为txt文件的方法
  4. docker容器的本地局域网yum源优化
  5. [PyJs系列介绍]三、编译与上线
  6. Android开发之通过反射获取全局的application
  7. java mybatis 代码生成器_Java MyBatis-Plus 代码生成器
  8. oracle 次月,Oracle日期查询:季度、月份、星期等时间信息
  9. BSD配置SSH服务
  10. python 最快 因式分解_Python实现的对一个数进行因式分解操作示例
  11. 研究CV、研究美,MMFashion开源库升级~
  12. VxWorks程序一下载就停住了
  13. CodeForces 785C Anton and Fairy Tale 二分
  14. linux mysql ssh通道_通过SSH通道来访问MySQL
  15. 删除文件显示正在使用无法删除该怎么办?
  16. 如何用Jupyter中文集成版画一个图表
  17. 【挖矿程式】oCam 萤幕录影软体竟然藏有挖矿程式 (BRTSvc)(转)
  18. 交换两个变量的值的swap函数
  19. Windows 系统文件资源管理器的命令行参数(如何降权打开程序,如何选择文件)
  20. 2020“智创未来 · 精彩平度”创新创业大赛阶段性成果概览

热门文章

  1. 射频识别系统及WMS仓库管理系统功能介绍
  2. linux键盘符号错乱,Ubuntu14.04 键盘错位小问题
  3. 专访中科云创周北川:用AI来做工业设备的故障诊断,目前还停留在“鸡尾酒疗法”阶段
  4. python-生产者消费者模型_线程_线程互斥锁_GIL全局解释器锁
  5. 怎样学好英语?(多年英语学习经验总结)
  6. java中任何变量都可以被赋值为null,java中当给一个对象赋值为null时发生了什么...
  7. FPGA电源设计方案
  8. cfg格式文件在服务器哪里,cfg文件,小编教你怎么打开cfg文件
  9. 四川一度智信|小白卖家不懂选品?
  10. # Sql语句过长报错、查询慢优化方案探索