Android FFmpeg02 --- 播放本地视频
2022-04-14 本文已影响0人
沪漂意哥哥
一. MainActivity
public class MainActivity extends AppCompatActivity {
Surface surface;
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
checkPermission();
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surface);
final SurfaceHolder surfaceViewHolder = surfaceView.getHolder();
surfaceViewHolder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
surface = surfaceViewHolder.getSurface();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { }
@Override
public void surfaceDestroyed(SurfaceHolder holder) { }
});
}
public boolean checkPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(
Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.CAMERA
}, 1);
}
return false;
}
public void play(View view) {
String folderurl = new File(Environment.getExternalStorageDirectory(), "input.mp4").getAbsolutePath();
play(folderurl, surface);
}
public native int play(String url, Surface surface);
}
二. build.gradle
android {
compileSdkVersion 28
buildToolsVersion "28.0.3"
defaultConfig {
applicationId "com.luisliuyi.demo.ffmpeg"
minSdkVersion 16
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
abiFilters 'armeabi-v7a'
}
}
ndk{
abiFilters 'armeabi-v7a'
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
sourceSets {
main {
jniLibs.srcDirs = ['src/main/cpp/libs']
}
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.10.2"
}
}
}
三.CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
include_directories(include)
#添加系统环境变量
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}")
add_library(
native-lib
SHARED
native-lib.cpp)
find_library(
log-lib
log)
target_link_libraries(
native-lib
avcodec
avdevice
avfilter
avformat
avutil
swresample
swscale
android
${log-lib})
四.native-lib.cpp
#include <jni.h>
#include <string>
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/imgutils.h"
#include "libswscale/swscale.h"
#include <libavutil/time.h>
}
#include <android/log.h>
#include <android/native_window_jni.h>
#define LOGD(...) __android_log_print(ANDROID_LOG_INFO,"liuyi",__VA_ARGS__)
static AVFormatContext *avFormatContext;
static AVCodecContext *avCodecContext;
AVCodec *vCodec;
ANativeWindow* nativeWindow;
ANativeWindow_Buffer windowBuffer;
static AVPacket *avPacket;
static AVFrame *avFrame, *rgbFrame;
uint8_t *outbuffer;
struct SwsContext *swsContext;
extern "C"
JNIEXPORT jint JNICALL
Java_com_luisliuyi_demo_ffmpeg_MainActivity_play(JNIEnv *env, jobject thiz, jstring url_,
jobject surface) {
const char *url = env->GetStringUTFChars(url_, 0);
//注册所有的组件
avcodec_register_all();
avformat_network_init();
//实例化了上下文
avFormatContext=avformat_alloc_context();
//打开文件
if(avformat_open_input(&avFormatContext,url,NULL,NULL)!=0){
LOGD("Couldn't open input stream.\n");
return -1;
}
LOGD("打开视频成功.\n");
//查找文件的流信息 mp4 失败 的视频
if(avformat_find_stream_info(avFormatContext,NULL)<0){
LOGD("Couldn't find stream information.\n");
return -1;
}
//视频流索引
int videoindex = -1;
for(int i=0; i<avFormatContext->nb_streams; i++) {
if (avFormatContext->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_VIDEO) {
videoindex = i;
break;
}
}
if(videoindex == -1){
LOGD("Couldn't find a video stream.\n");
return -1;
}
LOGD("找到了视频流\n");
env->ReleaseStringUTFChars(url_, url);
//解码器上下文
avCodecContext=avFormatContext->streams[videoindex]->codec;
vCodec=avcodec_find_decoder(avCodecContext->codec_id);
//打开解码器
if(avcodec_open2(avCodecContext, vCodec,NULL)<0){
LOGD("Couldn't open codec.\n");
return -1;
}
LOGD("打开了解码成功\n");
//获取界面传下来的surface
nativeWindow = ANativeWindow_fromSurface(env, surface);
if (0 == nativeWindow){
LOGD("Couldn't get native window from surface.\n");
return -1;
}
avFrame = av_frame_alloc();
avPacket = av_packet_alloc();
rgbFrame = av_frame_alloc();
int width = avCodecContext->width;
int height = avCodecContext->height;
int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGBA, width, height,1 );
//实例化一个输入缓冲区
outbuffer = (uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
//缓冲区 设置给 rgbferame
av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize, outbuffer, AV_PIX_FMT_RGBA, width,
height, 1);
//转换器
swsContext = sws_getContext(width, height, avCodecContext->pix_fmt,
width, height, AV_PIX_FMT_RGBA, SWS_BICUBIC, NULL, NULL, NULL);
if (0 > ANativeWindow_setBuffersGeometry(nativeWindow,width,height,WINDOW_FORMAT_RGBA_8888)){
LOGD("Couldn't set buffers geometry.\n");
ANativeWindow_release(nativeWindow);
return -1;
}
while (av_read_frame(avFormatContext, avPacket)>=0) {
if (avPacket->stream_index == videoindex) {
int ret = avcodec_send_packet(avCodecContext, avPacket);
if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF){
LOGD("解码出错");
return -1;
}
ret = avcodec_receive_frame(avCodecContext, avFrame);
if (ret == AVERROR(EAGAIN)) {
continue;
} else if (ret < 0) {
break;
}
sws_scale(swsContext, avFrame->data, avFrame->linesize, 0, avCodecContext->height,
rgbFrame->data, rgbFrame->linesize);
if (ANativeWindow_lock(nativeWindow, &windowBuffer, NULL) < 0) {
LOGD("cannot lock window");
} else {
uint8_t *dst = (uint8_t *) windowBuffer.bits;
for (int h = 0; h < height; h++) {
memcpy(dst + h * windowBuffer.stride * 4, outbuffer + h * rgbFrame->linesize[0],
rgbFrame->linesize[0]);
}
switch(avFrame->pict_type){
case AV_PICTURE_TYPE_I:
LOGD("I");
break;
case AV_PICTURE_TYPE_P:
LOGD("P");
break;
case AV_PICTURE_TYPE_B:
LOGD("B");
break;
default:
;break;
}
}
av_usleep(1000 * 33);
ANativeWindow_unlockAndPost(nativeWindow);
}
}
avformat_free_context(avFormatContext);
env->ReleaseStringUTFChars(url_, url);
return -1;
}
五.代码地址
https://gitee.com/luisliuyi/android-ffmpeg01.git