Android使用FFmpeg 解码H264并播放(三)
2017-06-12 18:01
603 查看
上一节记录了Android使用FFmpeg解码H264的过程。这一节记录在Android上播放的过程。
AVFrame 格式转化
一般从视频流里解析出的 AVFrame 的格式是 yuv 的,不能直接在 Android 的 nativewindow 上使用,需要先转换为 ARGB 或者 RGB565才行。
转码的代码网上很多,大致如下:
渲染画面到 NativeWindow
从surface 中获取 NativeWindow
设置NativeWindow缓冲区格式
锁定NativeWindow缓冲区
写入数据到缓冲区
释放缓冲区
如果不再渲染了,释放NativeWindow
代码如下:
H264FrameRender.java
JNI
渲染
解码器和格式转换器完成后会将调用渲染方法,将最终的数据渲染到
问题描述
在开发中使用某摄像头的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
从surface 中获取 NativeWindow
设置NativeWindow缓冲区格式
锁定NativeWindow缓冲区
写入数据到缓冲区
释放缓冲区
如果不再渲染了,释放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并播放(二)
- Android使用FFmpeg 解码H264并播放(一)
- Android平台上使用SDL官方demo播放视频(使用ffmpeg最新版解码)
- Android平台上使用SDL官方demo播放视频(使用ffmpeg最新版解码)
- 使用jni调用ffmpeg.so中的H264解码函数播放文件
- Android平台上使用SDL官方demo播放视频(使用ffmpeg最新版解码)
- Android FFMpeg(三)——使用FFMpeg解码h264、aac
- Android平台上使用SDL官方demo播放视频(使用ffmpeg最新版解码)
- ffmpeg将yuv编码成h264数据量变小且可以使用暴风影音播放出来,很短一闪而过
- linux下mplayer(ffmpeg)通过x264解码播放高清h264视频
- [置顶] Android使用MediaCodec硬解码播放H264格式视频文件
- 在iOS平台使用ffmpeg解码h264视频流
- Android+FFmpeg+OpenSL ES音频解码播放
- Live555接收h264使用ffmpeg解码为YUV420 .
- Android利用mediacodec进行视频H264编码解码播放
- Android 解码MediaCodec 播放H264 265
- Android+FFmpeg+ANativeWindow视频解码播放
- 在iOS平台使用ffmpeg解码h264视频流
- android 4.4 H264 ffmpeg编解码
- Live555接收h264使用ffmpeg解码为YUV420