本篇项目地址,求star
https://github.com/979451341/Audio-and-video-learning-materials/tree/master/FFmpeg%E6%92%AD%E6%94%BE%E8%A7%86%E9%A2%91java
首先FFmpeg是c语言写的,因此咱们须要NDK的技术,而后我使用的NDK使用Cmake的,一开始就是说如何将FFmpeg导入项目,使用个人方法导入FFmpeg不用一分钟。android
这个须要你们先在上面的代码地址里下载项目代码
由于FFmpeg这个基于android的so文件如何生成的我不写出来,我也是直接用别人文件,直接使用我项目里的就行了git
1.FFmpeg简单的说明github
多媒体视频处理工具FFmpeg有很是强大的功能包括视频采集功能、视频格式转换、视频抓图、给视频加水印等。app
他的功能有7大部分完整ide
libavcodec:提供范围更广的编×××的实现。函数
libavformat:实现流媒体协议,容器格式和基本的I/O访问。工具
libavutil:包括校验,解压缩和各类实用功能。学习
libavfilter:提供了一个平均改变解码音频和视频经过过滤器链。gradle
libavdevice:提供抽象访问捕获和重放设备。
libswresample:实现音频混合和重采样程序。
libswscale:实现颜色转换和缩放程序。
2.环境配置
将下载的项目里jniLibs和cpp粘贴到本身建立的项目的main文件夹下
我还须要在app module的build.gradle添加代码,在defaultConfig里添加ndk支持的类型,还有给Cmake添加参数,在android下导入CMakeLists文件,例子代码以下:
android {
compileSdkVersion 26
defaultConfig {
applicationId "jonesx.videoplayer"
minSdkVersion 19
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
ndk {
abiFilters 'armeabi'
}
externalNativeBuild {
cmake {
arguments '-DANDROID_TOOLCHAIN=clang','-DANDROID_STL=gnustl_static'
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
}
}
}
3.代码说明
首先就是可以使用cpp文件夹下的VideoPlayer的代码,那咱们就须要建立一个VideoPlayer的java类
public class VideoPlayer {
static { System.loadLibrary("VideoPlayer"); } public static native int play(Object surface);
}
使用这个play函数,直接在SurfaceView的surfaceCreated函数里开启线程使用
@Override public void surfaceCreated(SurfaceHolder holder) { new Thread(new Runnable() { @Override public void run() { VideoPlayer.play(surfaceHolder.getSurface()); } }).start(); }
那重点来了,说一说VideoPlayer用到了FFmpeg哪些东西
获取视频格式的环境,打开MP4文件
AVFormatContext *pFormatCtx = avformat_alloc_context();
if (avformat_open_input(&pFormatCtx, file_name, NULL, NULL) != 0) { LOGD("Couldn't open file:%s\n", file_name); return -1; // Couldn't open file }
查看是否有流,若是那就看是否有视频流
if (avformat_find_stream_info(pFormatCtx, NULL) < 0) { LOGD("Couldn't find stream information."); return -1; } int videoStream = -1, i; for (i = 0; i < pFormatCtx->nb_streams; i++) { if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && videoStream < 0) { videoStream = i; } } if (videoStream == -1) { LOGD("Didn't find a video stream."); return -1; // Didn't find a video stream }
得到视频×××环境,而后看这个×××是否可以开启
AVCodecContext *pCodecCtx = pFormatCtx->streams[videoStream]->codec; // Find the decoder for the video stream AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id); if (pCodec == NULL) { LOGD("Codec not found."); return -1; // Codec not found } if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) { LOGD("Could not open codec."); return -1; // Could not open codec }
经过surface获取目前手机屏幕给这个Surface的内存空间
// 获取native window ANativeWindow *nativeWindow = ANativeWindow_fromSurface(env, surface); // 获取视频宽高 int videoWidth = pCodecCtx->width; int videoHeight = pCodecCtx->height; // 设置native window的buffer大小,可自动拉伸 ANativeWindow_setBuffersGeometry(nativeWindow, videoWidth, videoHeight, WINDOW_FORMAT_RGBA_8888); ANativeWindow_Buffer windowBuffer; if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) { LOGD("Could not open codec."); return -1; // Could not open codec }
转格式
struct SwsContext *sws_ctx = sws_getContext(pCodecCtx->width,
pCodecCtx->height,
pCodecCtx->pix_fmt,
pCodecCtx->width,
pCodecCtx->height,
AV_PIX_FMT_RGBA,
SWS_BILINEAR,
NULL,
NULL,
NULL);
首先这个解码是在一个循环里,而后解码,和以前同样一帧一帧的解码,可是若是一帧太大那就下一次循环里继续解码
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
释放资源
av_free(buffer); av_free(pFrameRGBA); // Free the YUV frame av_free(pFrame); // Close the codecs avcodec_close(pCodecCtx); // Close the video file avformat_close_input(&pFormatCtx);
完了,说是完了,这只是开始,我对FFmpeg的学习也是开始,之后我可能断断续续的分享我使用FFmpeg的心得。