当前位置:   article > 正文

五种方案实现视频镜像_textureview 镜像

textureview 镜像

视频镜像一般是指,以视频x轴中心点为对称轴,左右内容互相交换。实现视频镜像功能,可以从解码层、渲染层、显示层这三个层次入手。解码层需要对解码出来的每一帧进行镜像处理,以FFmpeg软解处理为例,比较耗时,也占用更多内存空间,从性能效率角度考虑不太可取。但是,可以同时添加滤镜、模糊效果、文字与动画贴纸等等。当然,渲染层使用openGL也可以实现这些功能,同时也可以做镜像。显示层如果使用TextureView,可以通过设置旋转实现镜像,用法最为简单。

一、显示层

视频播放一般使用SurfaceView、GLSurfaceView或者TextureView来做显示控件。SurfaceView有独立Surface,有前台、后台双缓冲,效率比较高,但不具有View属性,不能做旋转、平移、缩放、透明度等动画;GLSurfaceView继承于SurfaceView,封装一个GLThread作为渲染线程;TextureView继承于View,具有View的属性,可以做任意属性动画,但是存在缓存,有2、3帧延迟,渲染显示效率比较低一点。在这里针对TextureView控件,来实现视频镜像。

1、setRotationY

setRotationY属于View提供的方法,内部实现原理是使用RenderNode进行旋转,在旋转前后分别调用invalidateViewProperties方法,具体方法如下:

  1. public void setRotationY(float rotationY) {
  2. if (rotationY != getRotationY()) {
  3. invalidateViewProperty(true, false);
  4. mRenderNode.setRotationY(rotationY);
  5. invalidateViewProperty(false, true);
  6. invalidateParentIfNeededAndWasQuickRejected();
  7. notifySubtreeAccessibilityStateChangedIfNeeded();
  8. }
  9. }

外部只需一行代码即可实现镜像:

mView.setRotationY(180);

取消镜像只需:把旋转角度设为0:

mView.setRotationY(0);

2、带翻转动画的ObjectAnimator

如果单纯是镜像不够酷,我们可以加个3D翻转动画,使用ObjectAnimator实现:

  1. Animator animator = ObjectAnimator.ofFloat(mView, "rotationY", 180);
  2. animator.setDuration(500).start();

同样地,取消镜像只需把旋转角度设为0:

  1. Animator animator = ObjectAnimator.ofFloat(mView, "rotationY", 0);
  2. animator.setDuration(500).start();

3、利用Matrix的setScale

setScale是Matrix矩阵提供的方法,然后使用TextureView的setTransform方法把Matrix矩阵设置进去,实现左右镜像需要View的宽度的一半作为镜像中心轴:

  1. Matrix matrix = textureView.getMatrix();
  2. matrix.setScale(-1, 1, textureView.getWidth()/2, 0);
  3. textureView.setTransform(matrix);

取消镜像只要-1改为1,去掉x轴中心点:

  1. Matrix matrix = textureView.getMatrix();
  2. matrix.setScale(1, 1);
  3. textureView.setTransform(matrix);

二、渲染层

如果时使用openGL进行渲染, 可以修改顶点着色器的顶点坐标,取x坐标点与1的补数:

  1. attribute vec4 vPosition;
  2. attribute vec2 vCoord;
  3. varying vec2 fCoord;
  4. void main() {
  5. gl_Position = vPosition;
  6. fCoord = vec2(1-vCoord.x, vCoord.y);
  7. }

取消镜像只要把vCoord直接赋值给fCoord:

  1. attribute vec4 vPosition;
  2. attribute vec2 vCoord;
  3. varying vec2 fCoord;
  4. void main() {
  5. gl_Position = vPosition;
  6. fCoord = vCoord;
  7. }

但是,openGL实现镜像有个不便之处是,需要重新加载shader,把新的glsl语言加载进去,链接到program程序中。存在销毁重新创建过程,也就是导致一刹那黑屏或者卡顿。相关的glsl语言语法,可以参考:glsl语言的基本语法

三、解码层

如果是使用FFmpeg软解码,可以利用AVFilter模块进行视频镜像,对每一个视频帧进行处理,同时filter可以叠加,做滤镜、贴纸、模糊等特效。filter的使用可参考:AVFilter模块使用步骤

1、初始化AVFilter

  1. int init_filters(const char *filters_descr) {
  2. char args[512];
  3. int ret = 0;
  4. AVFilter *buffersrc = avfilter_get_by_name("buffer");
  5. AVFilter *buffersink = avfilter_get_by_name("buffersink");
  6. AVFilterInOut *outputs = avfilter_inout_alloc();
  7. AVFilterInOut *inputs = avfilter_inout_alloc();
  8. AVRational time_base = pFormatCtx->streams[video_stream_index]->time_base;
  9. enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE};
  10. filter_graph = avfilter_graph_alloc();
  11. if (!outputs || !inputs || !filter_graph) {
  12. ret = AVERROR(ENOMEM);
  13. goto end;
  14. }
  15. /* buffer video source: the decoded frames from the decoder will be inserted here. */
  16. snprintf(args, sizeof(args),
  17. "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
  18. pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
  19. time_base.num, time_base.den,
  20. pCodecCtx->sample_aspect_ratio.num, pCodecCtx->sample_aspect_ratio.den);
  21. ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in",
  22. args, NULL, filter_graph);
  23. if (ret < 0) {
  24. LOGE(TAG, "Cannot create buffer source\n");
  25. goto end;
  26. }
  27. /* buffer video sink: to terminate the filter chain. */
  28. ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out",
  29. NULL, NULL, filter_graph);
  30. if (ret < 0) {
  31. LOGE(TAG, "Cannot create buffer sink\n");
  32. goto end;
  33. }
  34. ret = av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts,
  35. AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
  36. if (ret < 0) {
  37. LOGE(TAG, "Cannot set output pixel format\n");
  38. goto end;
  39. }
  40. outputs->name = av_strdup("in");
  41. outputs->filter_ctx = buffersrc_ctx;
  42. outputs->pad_idx = 0;
  43. outputs->next = NULL;
  44. inputs->name = av_strdup("out");
  45. inputs->filter_ctx = buffersink_ctx;
  46. inputs->pad_idx = 0;
  47. inputs->next = NULL;
  48. if ((ret = avfilter_graph_parse_ptr(filter_graph, filters_descr,
  49. &inputs, &outputs, NULL)) < 0)
  50. goto end;
  51. if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0)
  52. goto end;
  53. end:
  54. avfilter_inout_free(&inputs);
  55. avfilter_inout_free(&outputs);
  56. return ret;
  57. }

2、添加进filter队列

av_buffersrc_add_frame_flags(buffersrc_ctx, pFrame, AV_BUFFERSRC_FLAG_KEEP_REF);

3、从filter队列读取

 av_buffersink_get_frame(buffersink_ctx, filter_frame);

4、释放资源

  1. avfilter_free(buffersrc_ctx);
  2. avfilter_free(buffersink_ctx);
  3. avfilter_graph_free(&filter_graph);

以上是实现视频镜像的几种方案,各位客官可以根据实际场景来选择。没有最好的,只有最合适的。音视频学习和音视频处理项目可参考:https://github.com/xufuji456/FFmpegAndroid

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/249839
推荐阅读
相关标签
  

闽ICP备14008679号