总思路
1.lock window
2.缓冲区赋值
3.unlock window
和FFmpeg转码一样
导入对应的头文件和so库
1)新建VideoView
public class VideoView extends SurfaceView {
public VideoView(Context context) {
super(context);
}
public VideoView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public VideoView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
/**
* 初始化,SurfaceView绘制的像素格式
*/
private void init() {
SurfaceHolder surfaceHolder = getHolder();
surfaceHolder.setFormat(PixelFormat.RGBA_8888);
}
}
2)新建JasonPlayer
public class JasonPlayer {
public native void render(String input, Surface surface);
static{
System.loadLibrary("avutil-54");
System.loadLibrary("swresample-1");
System.loadLibrary("avcodec-56");
System.loadLibrary("avformat-56");
System.loadLibrary("swscale-3");
System.loadLibrary("postproc-53");
System.loadLibrary("avfilter-5");
System.loadLibrary("avdevice-56");
System.loadLibrary("myffmpeg");
}
}
3)javah 生成头文件
com_boom_audioplayer_JasonPlayer.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_boom_audioplayer_JasonPlayer */
#ifndef _Included_com_boom_audioplayer_JasonPlayer
#define _Included_com_boom_audioplayer_JasonPlayer
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_boom_audioplayer_JasonPlayer
* Method: render
* Signature: (Ljava/lang/String;Ljava/lang/Object;)V
*/
JNIEXPORT void JNICALL Java_com_boom_audioplayer_JasonPlayer_render
(JNIEnv *, jobject, jstring, jobject);
#ifdef __cplusplus
}
#endif
#endif
4)新建do_video_player.c
#include "com_boom_audioplayer_JasonPlayer.h"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <android/log.h>
#include <android/native_window_jni.h>
#include <android/native_window.h>
#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"jason",FORMAT,##__VA_ARGS__);
#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"jason",FORMAT,##__VA_ARGS__);
#include "libyuv.h"
//封装格式
#include "libavformat/avformat.h"
//解码
#include "libavcodec/avcodec.h"
//缩放
#include "libswscale/swscale.h"
JNIEXPORT void JNICALL Java_com_boom_audioplayer_JasonPlayer_render
(JNIEnv *env, jobject jobj, jstring input_jstr, jobject surface){
const char* input_cstr = (*env)->GetStringUTFChars(env,input_jstr,NULL);
//1.注册组件
av_register_all();
//封装格式上下文
AVFormatContext *pFormatCtx = avformat_alloc_context();
//2.打开输入视频文件
if(avformat_open_input(&pFormatCtx,input_cstr,NULL,NULL) != 0){
LOGE("%s","打开输入视频文件失败");
return;
}
//3.获取视频信息
if(avformat_find_stream_info(pFormatCtx,NULL) < 0){
LOGE("%s","获取视频信息失败");
return;
}
//视频解码,需要找到视频对应的AVStream所在pFormatCtx->streams的索引位置
int video_stream_idx = -1;
int i = 0;
for(; i < pFormatCtx->nb_streams;i++){
//根据类型判断,是否是视频流
if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){
video_stream_idx = i;
break;
}
}
//4.获取视频解码器
AVCodecContext *pCodeCtx = pFormatCtx->streams[video_stream_idx]->codec;
AVCodec *pCodec = avcodec_find_decoder(pCodeCtx->codec_id);
if(pCodec == NULL){
LOGE("%s","无法解码");
return;
}
//5.打开解码器
if(avcodec_open2(pCodeCtx,pCodec,NULL) < 0){
LOGE("%s","解码器无法打开");
return;
}
//编码数据
AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket));
//像素数据(解码数据)
AVFrame *yuv_frame = av_frame_alloc();
AVFrame *rgb_frame = av_frame_alloc();
//native绘制
//窗体
ANativeWindow* nativeWindow = ANativeWindow_fromSurface(env,surface);
//绘制时的缓冲区
ANativeWindow_Buffer outBuffer;
int len ,got_frame, framecount = 0;
//6.一阵一阵读取压缩的视频数据AVPacket
while(av_read_frame(pFormatCtx,packet) >= 0){
//解码AVPacket->AVFrame
len = avcodec_decode_video2(pCodeCtx, yuv_frame, &got_frame, packet);
//Zero if no frame could be decompressed
//非零,正在解码
if(got_frame){
LOGI("解码%d帧",framecount++);
//lock
//设置缓冲区的属性(宽、高、像素格式)
ANativeWindow_setBuffersGeometry(nativeWindow, pCodeCtx->width, pCodeCtx->height,WINDOW_FORMAT_RGBA_8888);
ANativeWindow_lock(nativeWindow,&outBuffer,NULL);
//设置rgb_frame的属性(像素格式、宽高)和缓冲区
//rgb_frame缓冲区与outBuffer.bits是同一块内存
avpicture_fill((AVPicture *)rgb_frame, outBuffer.bits, AV_PIX_FMT_RGBA, pCodeCtx->width, pCodeCtx->height);
//YUV->RGBA_8888
I420ToARGB(yuv_frame->data[0],yuv_frame->linesize[0],
yuv_frame->data[2],yuv_frame->linesize[2],
yuv_frame->data[1],yuv_frame->linesize[1],
rgb_frame->data[0], rgb_frame->linesize[0],
pCodeCtx->width,pCodeCtx->height);
//unlock
ANativeWindow_unlockAndPost(nativeWindow);
usleep(1000 * 16);
}
av_free_packet(packet);
}
ANativeWindow_release(nativeWindow);
av_frame_free(&yuv_frame);
avcodec_close(pCodeCtx);
avformat_free_context(pFormatCtx);
(*env)->ReleaseStringUTFChars(env,input_jstr,input_cstr);
}
5)添加权限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
Android.mk和Application.mk之前一样