本篇項目地址,求starhttps://github.com/979451341/Audio-and-video-learning-materials/tree/master/FFmpeg%E6%92%AD%E6%94%BE%E8%A7%86%E9%A2%91首先FFmpeg是c語言寫的,所以我們需 ...
本篇項目地址,求star
https://github.com/979451341/Audio-and-video-learning-materials/tree/master/FFmpeg%E6%92%AD%E6%94%BE%E8%A7%86%E9%A2%91
首先FFmpeg是c語言寫的,所以我們需要NDK的技術,然後我使用的NDK使用Cmake的,一開始就是說如何將FFmpeg導入項目,使用我的方法導入FFmpeg不用一分鐘。
這個需要大家先在上面的代碼地址里下載項目代碼
因為FFmpeg這個基於android的so文件如何生成的我不寫出來,我也是直接用別人文件,直接使用我項目里的就好了
1.FFmpeg簡單的說明
多媒體視頻處理工具FFmpeg有非常強大的功能包括視頻採集功能、視頻格式轉換、視頻抓圖、給視頻加水印等。
他的功能有7大部分完整
libavcodec:提供範圍更廣的編解碼器的實現。
libavformat:實現流媒體協議,容器格式和基本的I/O訪問。
libavutil:包括校驗,解壓縮和各種實用功能。
libavfilter:提供了一個平均改變解碼音頻和視頻通過過濾器鏈。
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的心得。