Android硬编、硬解h264

项目工程demo地址https://github.com/liluojun/PlayVideo

demo包含硬编解h264、libyuv裁剪图像、opengles渲染yuv数据、ffmpeg解码裸h264数据等功能,故仅供参考测试。

硬编码首先设置编码器

MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", width, height);

mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);//色彩空间

mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);//码率

mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, m_framerate);//帧率

mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 2);//I帧间隔

try {

mediaCodec = MediaCodec.createEncoderByType("video/avc");

} catch (Exception e) {

e.printStackTrace();

}

mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

mediaCodec.start();//启动编码器

这块基本是模板代码,编码器常用的参数都在这里。

喂数据编码,数据来源手机相机

public void encoder(byte[] input) {

if (isRuning) {

try {

if (input != null && input.length != 0) {

if (colorFormat <= 20) {

JavaToNativeMethod.getInstence().nv21ToI420(input, yuv420sp, m_width, m_height, yByte, uByte, vByte);

} else {

JavaToNativeMethod.getInstence().nv21ToNv12(input, yuv420sp, m_width, m_height, yByte, uByte, vByte);

}

}

if (!encode) {

return;

}

if (input != null && input.length != 0) {

try {

ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();

ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();

int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);

Log.e("index", "" + inputBufferIndex);

if (inputBufferIndex >= 0) {

pts = computePresentationTime(generateIndex);

ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];

inputBuffer.clear();

inputBuffer.put(input);

mediaCodec.queueInputBuffer(inputBufferIndex, 0, input.length, pts, 0);

generateIndex += 1;

} else {

return;

}

MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();

int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);

while (outputBufferIndex >= 0) {

UiVideoData u = new UiVideoData();

ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];

byte[] outData = new byte[bufferInfo.size];

outputBuffer.get(outData);

}

bufferInfo = null;

} catch (Exception e) {

Log.e("Encoder", "编码错误" + e.getMessage());

}

}

} catch (Exception e) {

e.printStackTrace();

}

}

}

转换数据格式我使用的是Android手机默认的nv21格式,但是受到手机存储yuv数据格式的限制故需要转换格式,格式转换用的是libyuv库,代码如下:

if (colorFormat <= 20) {

JavaToNativeMethod.getInstence().nv21ToI420(input, yuv420sp, m_width, m_height, yByte, uByte, vByte);

} else {

JavaToNativeMethod.getInstence().nv21ToNv12(input, yuv420sp, m_width, m_height, yByte, uByte, vByte);

}

编码基本上是模板代码在 outputBuffer.get(outData);这里outdata就是编码后的数据了。

关于sps与pps的获取:

if (spsPpsBuffer.getInt() == 0x00000001) {

byte[] mMediaHead = new byte[outData.length];

System.arraycopy(outData, 0, mMediaHead, 0, outData.length);

configbyte = outData;

mMediaHead = null;

outData = null;

break;

}

在编码器启动后出的第一个数据就是sps与pps信息。

关于I帧的判断:

(outData[4] & 0x1f) == 5

这里我采用的是上面的判定方式,网上也有其他的判定方式如outData[4]==65||outData[4]==25之类的。

强制I帧有时候我们会遇到需要强行出一帧I帧的情况,只需要调用编码器的flush()方法即可。

硬解码首先也是设置解码器

mediaformat = MediaFormat.createVideoFormat("video/avc", w, h);

mediaformat.setByteBuffer("csd-0", ByteBuffer.wrap(sps));//设置sps

mediaformat.setByteBuffer("csd-1", ByteBuffer.wrap(pps));//设置pps

mediaformat.setInteger(MediaFormat.KEY_FRAME_RATE, m_framerate);//帧率

mediaformat.setInteger(MediaFormat.KEY_BIT_RATE, 1024 * 1000);//码率

mediaformat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 2);//I帧间隔

mCodec.configure(mediaformat, null, null, 0);//注意第二个参数可以填入surface,将解码数据直接渲染,这里我用的自己的渲染故填null

mCodec.start();//启动解码器

编解码器的设置差不多,只是多了sps和pps的参数设定。

喂数据解码

public void onFrame(UiVideoData uiVideoData) {

byte[] buf = uiVideoData._data;

ByteBuffer[] inputBuffers = mCodec.getInputBuffers();//MediaCodec在此ByteBuffer[]中获取输入数据

ByteBuffer[] outputBuffers = mCodec.getOutputBuffers(); // 解码后的数据

int inputBufferIndex = mCodec.dequeueInputBuffer(100);//获取输入缓冲区的索引

if (inputBufferIndex >= 0) {

long pts = computePresentationTime(generateIndex);

ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];

inputBuffer.clear();

inputBuffer.put(buf);//先获取缓冲区,再放入值

mCodec.queueInputBuffer(inputBufferIndex, 0, buf.length, pts, 0);//四个参数,第一个是输入缓冲区的索引,第二个是放入的数据大小,第三个是时间戳,保证递增就是

generateIndex += 1;

} else {

return;

}

MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();//用于描述解码得到的byte[]数据的相关信息

int outputBufferIndex = mCodec.dequeueOutputBuffer(bufferInfo, 1000);//-1代表一直等待,0表示不等待

Log.e(TAG, "outputBufferIndex=" + outputBufferIndex);

while (outputBufferIndex >= 0) {//大于等于0表示解码器有数据输出

ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];

byte[] outData = new byte[bufferInfo.size];

outputBuffer.get(outData);//将Buffer内的数据取出到字节数组中

mCodec.releaseOutputBuffer(outputBufferIndex, true);

outputBufferIndex = -1;

}

}

这里也基本是模板代码 outputBuffer.get(outData)获取到解码后的数据然后就可以自己去渲染的。

注:Android硬编硬解受限于设备并不保证所有设备都能运行成功。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354

推荐阅读更多精彩内容

  • 3、使用 MediaCodec创建之后,需要通过start()方法进行开启。MediaCodec有输入缓冲区队列和...
    韩瞅瞅阅读 1,303评论 0 1
  • 打包 视音频在传输过程中需要定义相应的格式,这样传输到对端的时候才能正确地被解析出来。 1、HTTP-FLV We...
    韩瞅瞅阅读 1,637评论 2 5
  • 原文:https://developer.android.com/reference/android/media/...
    thebestofrocky阅读 6,069评论 0 6
  • 三月二日晨。 半个月亮在南天悬着,犹如老天正侧着一只耳朵,谛听微山湖的动静。 这是微山湖中的独山湖。难得的晴朗终于...
    孔孟之乡阅读 771评论 0 0
  • 昨天,跟同学网上聊天,他现在在一家初创公司上班,这个公司是做微型超声设备的,后来就聊到微型超声怎么才能做好,他们已...
    翁翁yeah阅读 314评论 0 1