上一节记录了Android使用FFmpeg解码H264的过程。这一节记录在Android上播放的过程。

问题描述

在开发中使用某摄像头的SDK,只能获取到一帧帧的 H264 视频数据,不知道视频流地址,需要自己解码出图像并播放。

问题解决

Android 播放解码后的视频帧

在Android上播放视频的总体思路是在Native层从 Surface 获取 到ANativeWindow,通过修改 ANativeWindow 的显示 buffer 来更新 Surface 的画面,这样一帧帧更新,最终看到的就是动画。

AVFrame 格式转化

一般从视频流里解析出的 AVFrame 的格式是 yuv 的,不能直接在 Android 的 nativewindow 上使用,需要先转换为 ARGB 或者 RGB565才行。
转码的代码网上很多,大致如下:

//1. 准备一个容器来装转码后的数据
AVFrame dst_frame = av_frame_alloc();
//在解码上下文使用extradata解析出第一帧图像后,ctx的width和height,pix_format 写入了实际的视频宽高,像素格式
dst_frame->width = ctx->width;
dst_frame->height = ctx->height;
//2. 转码为ARGB,来给NativeWindow显示
dst_frame->format = AV_PIX_FMT_ARGB;
//3. 根据输入图像和输出图像的信息(宽、高、像素),初始化格式转换上下文
//应该重复使用该上下文,不要每一帧都初始化一次
struct SwsContext * swsCtx = sws_getContext(
src_frame->width, src_frame->height,(enum AVPixelFormat) src_frame->format,
src_frame->width, src_frame->height,(enum AVPixelFormat) dst_frame->format,
SWS_FAST_BILINEAR, NULL, NULL, NULL);
//4. 初始化之前准备的dst_frame的buffer
int buffer_size = av_image_get_buffer_size((enum AVPixelFormat) dst_frame->format,src_frame->width,src_frame->height, 1);
uint8_t * buffer = (uint8_t *) av_malloc(sizeof(uint8_t) * buffer_size );
//5. 绑定dst_frame和新申请的buffer
av_image_fill_arrays(dst_frame->data, dst_frame->linesize, buffer,(enum AVPixelFormat) dst_frame->format, dst_frame->width, dst_frame->height, 1);
//6. 转码
sws_scale(swsCtx , (const uint8_t *const *) src_frame->data,src_frame->linesize, 0, src_frame->height,dst_frame->data, dst_frame->linesize);                            
渲染画面到 NativeWindow
  1. 从surface 中获取 NativeWindow
  2. 设置NativeWindow缓冲区格式
  3. 锁定NativeWindow缓冲区
  4. 写入数据到缓冲区
  5. 释放缓冲区
  6. 如果不再渲染了,释放NativeWindow

代码如下:

  • H264FrameRender.java
/**
* 设置渲染的Surface
*/
public void setSurface(Surface surface) {if (nativeObject <= 0) {throw new IllegalStateException("H264FrameRender init failed");}_setSurface(this.nativeObject, surface);}
/**
* JNI
*/
private native void _setSurface(long nativeObject, Surface surface);
  • JNI
JNIEXPORT void JNICALL Java_com_demo_H264FrameRender__1setSurface(JNIEnv *env, jobject instance,jlong nativeObject, jobject surface){//H264Render *render = (H264Render *) nativeObject;//获取到NativeWindow,ANativeWindow* window = ANativeWindow_fromSurface(surface);//绑定当前的window到native 层的render上,等待渲染if(render->window){ANativeWindow_release(render->window);render->window = NULL;}render->window = window;
}
  • 渲染
    解码器和格式转换器完成后会将调用渲染方法,将最终的数据渲染到 NativeWindow 上。

/**
* @param render 渲染器
* @param data   解码和转换处理后的图像数据
* @param len    data 的长度
* @param width  视频的宽
* @param height 视频的高
*/
void render_rend(NativeRender *render, signed char *data, size_t len, int width, int height) {if (render->window == NULL) {LOGE("window == null");return;}//设置缓冲区(宽,高,像素格式)ANativeWindow_setBuffersGeometry(render->window, width, height, WINDOW_FORMAT_RGBA_8888);//绘制int32_t locked = ANativeWindow_lock(render->window, render->buffer, NULL);if (locked == 0) {//由于window的 stride 和 frame 的 stride 不同,需要逐行拷贝uint8_t *dst = (uint8_t *) render->buffer->bits;uint8_t *src = dst_frame->data[0];int dest_stride = buffer->stride * 4;int src_stride = dst_frame->linesize[0];int height = dst_frame->height, h;for (h = 0; h < height; h++) {memcpy(dst + h * dest_stride,src + h * src_stride,(size_t) src_stride);}ANativeWindow_unlockAndPost(render->window);} else {LOGD("failed to lock window");}}

Android使用FFmpeg 解码H264并播放(三)相关推荐

  1. ffmpeg解码H264缺少帧的解决办法

    最近用ffmpeg解码H264裸码流文件,发现解码总是少几帧.上网查了些资料,解决了. 当使用avcodec_decode_video2时,如果第三个参数的值为1,则表示完成一帧的解码,如果为0,表示 ...

  2. ffmpeg 切片花屏_利用ffmpeg解码H264,花屏,该如何解决

    利用ffmpeg解码H264,花屏 UINT CMP4File::VideoCap_Thread_Fun(void* pParam) { CMP4File  *pMP4File=(CMP4File*) ...

  3. 使用FFMPEG解码和OpenAL播放音乐

    使用FFMPEG解码和OpenAL播放音乐 OpenAL是一个开源的音效库,然而这里只用它来播放音乐.  FFMPEG负责把音乐解码并转换成指定的编码和采样率,然后送到OpenAL中播放.  (已在w ...

  4. android下实时传输h264并播放

    这周给安排了个任务,在android端读取H264原始视频数据,传一帧播一帧,播就播吧,还要Socket实时传输实时播,虽然并没有接触过,都无从下手,不过想到直播都是这么个形式,说明肯定是行得通的,而 ...

  5. Android使用MediaCodec解码H264视频解码器

    前些日子有写了一篇博客[Android使用MediaCodec硬解码播放H264格式视频文件](http://blog.csdn.net/true100/article/details/5399293 ...

  6. FFmpeg解码H264裸流并转换成opencv Mat

    感谢雷霄骅博士的在中文视频编解码的付出,http://blog.csdn.net/leixiaohua1020 最近要搞一些视频推流的事情,要解析H264裸流并且获取opencv格式的Mat数据给算法 ...

  7. (转)FFMPEG解码H264拼帧简解

    http://blog.csdn.net/ikevin/article/details/7649095 H264的I帧通常 0x00 0x00 0x00 0x01 0x67 开始,到下一个帧头开始之前 ...

  8. android ffmpeg 解码,如何在Android用FFmpeg解码图像

    白猪掌柜的 创建一个VideoPicture结构体用来保存解码出来的图像./** SDL_Lesson.c** Created on: Aug 12, 2014* Author: clarck*/#i ...

  9. FFmpeg解码的全能播放器

    http://www.eoeandroid.com/thread-53373-1-1.html

  10. 使用ffmpeg解码音频sdl(push)播放

    自定义播放器系列 第一章 视频渲染 第二章 音频(push)播放(本章) 第三章 音频(pull)播放 第四章 实现时钟同步 第五章 实现通用时钟同步 第六章 实现播放器 文章目录 自定义播放器系列 ...

最新文章

  1. APOC 15 Years Celebration
  2. Jsp获得Map中map.put(2, bb);此类的value值
  3. Eclipse插件安装的三种方法
  4. 查看mysql日志文件大小和数据库大小
  5. 企业绩效管理系统之平衡记分卡
  6. rsync 服务快速部署手册
  7. 全排列及相关扩展算法(七)——组合数的字典序(另含全章代码整理)
  8. java处理表单变量_jsp处理表单及JS和JAVA变量互传
  9. Atitit.spring体系结构大总结 1. Spel表达式解析 1 2. Srping mvc 1 3. Ioc 4 3.1. ApplicationContext在BeanFactory的基础
  10. Lattice FPGA 开发工具Diamond使用流程总结——安装
  11. 在龙芯3A3000上安装Gentoo
  12. Excel如何批量添加超链接
  13. [TypeScript]中字符和ASCII码转换
  14. stm32f107外设晶振8MHZ换成25MHZ 配置系统时钟72MHZ
  15. 如何让地面不起灰_水泥地面起灰怎么办?老师傅6招搞定了
  16. 网页引入谷歌字体_在网页中使用Google字体
  17. Linux即时通讯软件都有哪些?政企要怎么挑选?
  18. Spring Boot+Spring Security:记住我(Remember-Me): 基于简单加密token的方案 - 第25篇
  19. 两种常见的Vlan间通信的方式
  20. 几种网站压力测试工具调研与使用

热门文章

  1. 白平衡(WB:white balance)数值设置
  2. 美式期权定价python_美式期权baw定价的python实现
  3. 85人教版高中英语第一册第十一课 AT A TAILOR'S SHOP
  4. 计算机键盘运算符号输入,电脑上感叹号怎么打出来(电脑键盘符号大全)
  5. 黑客游戏(强烈推荐)攻防ctf
  6. html 自动切换tab栏,html 实现tab切换的示例代码
  7. 阙值,阈值,阀值,傻傻分不清
  8. MongoDB分片式高可用集群搭建
  9. acfun怎么下载视频
  10. 服装计算机辅助设计学什么,【计算机应用论文】服装设计的计算机辅助设计应用分析(共3905字)...