最近做了一个android项目用到编解码功能。大概需求是:通过摄像头拍摄一段视频,然后抽帧,生成一个短视频,以及倒序视频,刚开始直接用 H.264 编码格式,没有使用MP4容器封装。做了这些功能后,反而觉得使用MP4格式更加兼容各机型,减少BUG出现。举个明显例子:在Android硬编的时候,常常会用到 MediaCodec和MediaExtractor 相结合。但是,如果你用的 H.264 裸视频文件,MediaExtractor 的 setSource 函数会报异常,它在某些机型(如魅族Note2,系统是5.1)无法解析该视频文件。
      得到大概的需求后,最初我们使用FFmpeg来做视频编解码,所谓软件编解码。由于在处理的过程中速率太慢,且需要在解码后快速展示,所以该方案无法达到我们的预想效果(一个FFmpeg视频解码,并保存为jpeg例子:https://github.com/xiaoxiaoqingyi/ffmpeg-android-video-decoder)。但其也有一些优点,比如在兼容方面,颜色转换方面都做得很好,毕竟不是硬件编解码(国内这么多机型,你懂的),其次FFmpeg能输出指定帧,而Android硬解(MediaCodec)不能输出指定帧,需要输入好几帧到解码器,才能解码出一帧。目前我还是没有找到输入一帧解出一帧的方案,哪位大神知道的,可以指导指导。
       在软件编解码不太适合的情况下,就只能考虑用硬件编解码了(MediaCodec)。在前些日子,我参加了腾讯2017LIVE 直播开发者大会,了解到,现在的直播已经大部分使用硬件来编解码了。刚说了,有些机型不能使用MediaExtractor来解析 h.264文件,为了兼容大部分的机型,需要自己来解析,通过分析h.264文件的每一个字节区分每一帧位置且是什么类型帧。实现该需求,首先在从摄像头获取的数据,如果使用 Camera,一般设置为NV21格式, 但有些人使用Camera2,设置的格式是IMAGE。不管是哪种格式,最终都需要转换成yuv420sp或yuv420p(注意:在转码时候,最好使用jni,用C/C++来转格式,效率会高很多倍),才能供MediaCodec编码,然后保存h.264文件。在创建MediaCodec实例化的时候,除了设置必须参数外,也要注意一些地方,比如,选择哪种编码器,一般情况会选择如下:
 MediaCodec.createEncoderByType("video/avc");
这看上去其实没什么问题,大概原理就是获取最优的Encoder,获取Android系统中编码器注册表最前的一个,一般都是硬件解码(MediaCodec也能调用软件编解码)。这样创建编码器其实不太靠谱,虽然官网也是这么推荐,但是在国内众多的Android机型中,有些手机就会出问题,有的编码出现蓝屏,有些直接就闪退了。有个国外的例子,大概的意思就是先获取 "video/avc" 类型的编码器,然后通过 try catch 一个个试验,如果没问题,就选用这个编码器。源码:https://github.com/ldm520/android_mediacodec_rtsp_h264
还有一个问题就是在设置 I 帧间隔的时候,有些手机不起作用,如下设置:
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, interval);
 
针对这种情况,需要使用另外一种设置I帧的方式,强制设置:
 
Bundle params = new Bundle();
params.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
mMediaCodec.setParameters(params);
 
在编解码时,当把所有的数据都输入编解码器的时候,要记得输入结束符,编解码器才会输出所有的帧。
 
    还有一个抽帧问题,如果使用MediaCodec来抽帧,生成一个新的视频。是否可以直接把H.264文件里的帧去掉就行了?这样不行的,通常会出现花屏。这需要重新把h.264文件输入到解码器,然后获取到自己想要的帧,再输入到一个新的编码器中,生成你想要的H.264文件。在这里还有一个格式问题,并不是从解码器解码出来的数据,就能直接使用编码器来编码,有部分手机可以,有些会出现蓝屏,甚至闪退的情况。这时候需要统一解码器处理的格式。如果你使用这种形式获取:
 
mMediaCodec.getOutputBuffer()
 
出来的格式各种各样,你很难去兼容。google已经推出了一种新的格式:
 
mMediaCodec.getOutputImage(outIndex)
 
得出的是一个Image 对象,该对象可以保存为 JPEG格式图片,也可以转换成NV21(参考:http://www.cnblogs.com/welhzh/p/6079631.html),像上面拍摄部分,转换成yuv422格式,再输入到编码器编码。这样不管什么机型都可以兼容了(我试用10多部不同厂商手机),虽然绕了很多弯路。
 
        在使用MediaCodec还是遇到比较多的问题,毕竟官网都说它是一个轻量的编解码器封装。该总结适合使用过MediaCodec或有一定的解码编码经验的童鞋们。如果你还没了解过 MediaCodec,可以参考官网:
 
     在使用MediaCodec的时候还遇到很多问题,这里没有一一列举出来, 欢迎有遇到同样问题或类似问题的童鞋留言讨论!

 
 

Android硬件编解码与软件编解码的更多相关文章

  1. 【GPU编解码】GPU硬解码---CUVID

    问题描述:项目中,需要对高清监控视频分析处理,经测试,其解码过程所占CPU资源较多,导致整个系统处理效率不高,解码成为系统的瓶颈. 解决思路: 利用GPU解码高清视频,降低解码所占用CPU资源,加速解 ...

  2. 使用多字节字符集的跨平台(PC、Android、IOS、WP)编码/解码方法

    随着移动端的发展,跨平台已成为通讯架构设计的重要考虑因素,PC.Android.IOS.WP等跨多平台间的数据通讯,必然要解决字符编码/解码的问题. 多字节字符集MBCS不是跨平台的首选字符集,面向跨 ...

  3. FFmpeg软件只是个解码编码软件,如果支持多种格式必须先安装好对应的库,下面就说下我装的库

    FFmpeg软件只是个解码编码软件,如果支持多种格式必须先安装好对应的库,下面就说下我装的库:1. 安装faad2 # wget http://downloads.sourceforge.net/fa ...

  4. EasyNVR智能云终端硬件与EasyNVR解决方案软件综合对比

    背景分析 互联网视频直播越来越成为当前视频直播的大势,对于传统的安防监控,一般都是局限于内网,无法成批量上云台.传统的海康和大华的平台虽然可以通过自身私有协议上云平台 集总管控,但是往往只是支持自身的 ...

  5. Android版Ftp服务端软件

    分享一款开发的Android版Ftp服务端软件,支持Android4.0及以上版本,可以实现局域网无线传输文件到手机,或者把手机上的多媒体文件分享到iPad等设备来扩展这些设备的存储空间,详情请见软件 ...

  6. Android服务器——使用TomCat实现软件的版本检测,升级,以及下载更新进度!

    Android服务器--使用TomCat实现软件的版本检测,升级,以及下载更新进度! 算下来,TomCat服务器已经写了很长一段时间了,一直说拿他来搞点事 情,也一直没做,今天刚好有空,交流群还有人请 ...

  7. C++的静态联编和动态联编

    联编的概念 联编是指一个计算机程序自身彼此关联的过程,在这个联编过程中,需要确定程序中的操作调用(函数调用)与执行该操作(函数)的代码段之间的映射关系. 意思就是这个函数的实现有多种,联编就是把调用和 ...

  8. C++_类继承3-动态联编和静态联编

    程序调用函数时,将使用哪个可执行代码块呢?编译器负责回答这个问题. 将源代码中的函数调用解释为特定的函数代码块被称为函数名联编(binding). 在C语言中,这非常简单,因为每个函数名对应一个不同的 ...

  9. C++的静态联编和动态联编详解

    一.概述: 通常来说联编就是将模块或者函数合并在一起生成可执行代码的处理过程,同时对每个模块或者函数调用分配内存地址,并且对外部访问也分配正确的内存地址,它是计算机程序彼此关联的过程.按照联编所进行的 ...

随机推荐

  1. -------- Rootkit 核心技术——利用 nt!_MDL 突破 KiServiceTable 的只读访问限制 Part II --------

    ------------------------------------------------------------------------------------------- 本篇开始进入正题 ...

  2. 以守护进程的方式部署flask

    1.文件目录 创建一个简单的flask 项目... application = Flask(__name__) application.debug = True 2.安装wsgi pip instal ...

  3. Python的交叉编译移植至arm板

    虽然网上有那么多python的交叉编译移植教程,但是方法差异蛮大,需要根据实际开发板的型号做调整,以下是适用于海思的板子移植过程. step 1. python版本从网上下就可以: step 2. 解 ...

  4. Javascript学习--时间

    digit = [ [ [0,0,1,1,1,0,0], [0,1,1,0,1,1,0], [1,1,0,0,0,1,1], [1,1,0,0,0,1,1], [1,1,0,0,0,1,1], [1, ...

  5. (2-2)SpringCloud-服务注册到Eureka Server集群并消费

    服务注册到Eureka Server集群 在(2-1)SpringCloue-Eureka实现高可用注册中心中我们搭建好了高可用的Eureka注册中心,下面我们要把服务注册到Eureka Server ...

  6. 针对php脚本文件执行锁定的代码,避免脚本在同一时间重复运行

    <?php//针对php脚本文件执行锁定的代码,避免脚本在同一时间重复运行,http://ken.01h.net/define('PHP_LOCK_FILE', dirname(__FILE__ ...

  7. python_如何读写csv数据

    案例: 通过股票网站,我们获取了中国股市数据集,它以csv数据格式存储 Data,Open,High,Low,Close,Volume,Adj Close 2016-06-28,8.63,8.47,8 ...

  8. 实现iota函数

    void Reverse(char *s) { char temp; char *p = s; char *q = s; while (*p != '\0') { p ++; } q --; whil ...

  9. Java中Unsafe类详解

    http://www.cnblogs.com/mickole/articles/3757278.html Java不能直接访问操作系统底层,而是通过本地方法来访问.Unsafe类提供了硬件级别的原子操 ...

  10. Python 字符串大小写操作

    #coding=utf-8 #python中字符串的操作 # 字符串的大小写 s='hello_wOrld_oF_you' upper_str = s.upper() print('全部大写: ',u ...