├── android-ffmpeg-tutorial01 ├── .classpath ├── .cproject ├── .project ├── AndroidManifest.xml ├── assets │ └── 1.mp4 ├── jni │ ├── Android.mk │ ├── Application.mk │ └── tutorial01.c ├── libs │ ├── android-support-v4.jar │ └── armeabi │ │ ├── libavcodec-55.so │ │ ├── libavformat-55.so │ │ ├── libavutil-52.so │ │ ├── libswscale-2.so │ │ └── libtutorial01.so ├── proguard-project.txt ├── project.properties ├── res │ ├── drawable-hdpi │ │ └── ic_launcher.png │ ├── drawable-mdpi │ │ └── ic_launcher.png │ ├── drawable-xhdpi │ │ └── ic_launcher.png │ ├── drawable-xxhdpi │ │ └── ic_launcher.png │ ├── layout │ │ └── activity_main.xml │ ├── values-sw600dp │ │ └── dimens.xml │ ├── values-sw720dp-land │ │ └── dimens.xml │ ├── values-v11 │ │ └── styles.xml │ ├── values-v14 │ │ └── styles.xml │ └── values │ │ ├── attrs_my_view.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml └── src │ └── roman10 │ └── tutorial │ └── android_ffmpeg_tutorial01 │ ├── MainActivity.java │ └── Utils.java ├── android-ffmpeg-tutorial02 ├── .classpath ├── .project ├── AndroidManifest.xml ├── assets │ ├── 1.mp4 │ └── 12.mp4 ├── jni │ ├── Android.mk │ ├── Application.mk │ └── tutorial02.c ├── libs │ ├── android-support-v4.jar │ └── armeabi │ │ ├── libavcodec-55.so │ │ ├── libavformat-55.so │ │ ├── libavutil-52.so │ │ ├── libswscale-2.so │ │ └── libtutorial02.so ├── proguard-project.txt ├── project.properties ├── res │ ├── drawable-hdpi │ │ └── ic_launcher.png │ ├── drawable-mdpi │ │ └── ic_launcher.png │ ├── drawable-xhdpi │ │ └── ic_launcher.png │ ├── drawable-xxhdpi │ │ └── ic_launcher.png │ ├── layout │ │ └── activity_main.xml │ ├── values-sw600dp │ │ └── dimens.xml │ ├── values-sw720dp-land │ │ └── dimens.xml │ ├── values-v11 │ │ └── styles.xml │ ├── values-v14 │ │ └── styles.xml │ └── values │ │ ├── attrs_my_view.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml └── src │ └── roman10 │ └── tutorial │ └── android_ffmpeg_tutorial02 │ ├── MainActivity.java │ └── Utils.java └── readme /android-ffmpeg-tutorial01/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/.cproject: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | android-ffmpeg-tutorial01 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.cdt.managedbuilder.core.genmakebuilder 10 | clean,full,incremental, 11 | 12 | 13 | ?children? 14 | ?name?=outputEntries\|?children?=?name?=entry\\\\\\\|\\\|?name?=entry\\\\\\\|\\\|\|| 15 | 16 | 17 | ?name? 18 | 19 | 20 | 21 | org.eclipse.cdt.make.core.append_environment 22 | true 23 | 24 | 25 | org.eclipse.cdt.make.core.buildArguments 26 | 27 | 28 | 29 | org.eclipse.cdt.make.core.buildCommand 30 | ndk-build 31 | 32 | 33 | org.eclipse.cdt.make.core.cleanBuildTarget 34 | clean 35 | 36 | 37 | org.eclipse.cdt.make.core.contents 38 | org.eclipse.cdt.make.core.activeConfigSettings 39 | 40 | 41 | org.eclipse.cdt.make.core.enableAutoBuild 42 | false 43 | 44 | 45 | org.eclipse.cdt.make.core.enableCleanBuild 46 | true 47 | 48 | 49 | org.eclipse.cdt.make.core.enableFullBuild 50 | true 51 | 52 | 53 | org.eclipse.cdt.make.core.stopOnError 54 | true 55 | 56 | 57 | org.eclipse.cdt.make.core.useDefaultBuildCmd 58 | true 59 | 60 | 61 | 62 | 63 | com.android.ide.eclipse.adt.ResourceManagerBuilder 64 | 65 | 66 | 67 | 68 | com.android.ide.eclipse.adt.PreCompilerBuilder 69 | 70 | 71 | 72 | 73 | org.eclipse.jdt.core.javabuilder 74 | 75 | 76 | 77 | 78 | com.android.ide.eclipse.adt.ApkBuilder 79 | 80 | 81 | 82 | 83 | org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder 84 | full,incremental, 85 | 86 | 87 | 88 | 89 | 90 | com.android.ide.eclipse.adt.AndroidNature 91 | org.eclipse.jdt.core.javanature 92 | org.eclipse.cdt.core.cnature 93 | org.eclipse.cdt.core.ccnature 94 | org.eclipse.cdt.managedbuilder.core.managedBuildNature 95 | org.eclipse.cdt.managedbuilder.core.ScannerConfigNature 96 | 97 | 98 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 12 | 13 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/assets/1.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman10/android-ffmpeg-tutorial/fc85bd0dc7971664139ee3e8d03601d6450dc2e0/android-ffmpeg-tutorial01/assets/1.mp4 -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | 5 | LOCAL_MODULE := tutorial01 6 | LOCAL_SRC_FILES := tutorial01.c 7 | LOCAL_LDLIBS := -llog -ljnigraphics -lz 8 | LOCAL_SHARED_LIBRARIES := libavformat libavcodec libswscale libavutil 9 | 10 | include $(BUILD_SHARED_LIBRARY) 11 | $(call import-module,ffmpeg-2.0.1/android/arm) 12 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := armeabi 2 | #APP_ABI := armeabi-v7a 3 | APP_PLATFORM := android-10 4 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/jni/tutorial01.c: -------------------------------------------------------------------------------- 1 | // tutorial01.c 2 | // 3 | // This tutorial was based on the code written by Stephen Dranger (dranger@gmail.com). 4 | // 5 | // The code is modified so that it can be compiled to a shared library and run on Android 6 | // 7 | // The code dumps first 5 fives of an input video file to /sdcard/android-ffmpeg-tutorial01 8 | // folder of the external storage of your Android device 9 | // 10 | // Feipeng Liu (http://www.roman10.net/) 11 | // Aug 2013 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | /*for android logs*/ 24 | #include 25 | 26 | #define LOG_TAG "android-ffmpeg-tutorial01" 27 | #define LOGI(...) __android_log_print(4, LOG_TAG, __VA_ARGS__); 28 | #define LOGE(...) __android_log_print(6, LOG_TAG, __VA_ARGS__); 29 | 30 | void SaveFrame(JNIEnv *pEnv, jobject pObj, jobject pBitmap, int width, int height, int iFrame) { 31 | char szFilename[200]; 32 | jmethodID sSaveFrameMID; 33 | jclass mainActCls; 34 | sprintf(szFilename, "/sdcard/android-ffmpeg-tutorial01/frame%d.jpg", iFrame); 35 | mainActCls = (*pEnv)->GetObjectClass(pEnv, pObj); 36 | sSaveFrameMID = (*pEnv)->GetMethodID(pEnv, mainActCls, "saveFrameToPath", "(Landroid/graphics/Bitmap;Ljava/lang/String;)V"); 37 | LOGI("call java method to save frame %d", iFrame); 38 | jstring filePath = (*pEnv)->NewStringUTF(pEnv, szFilename); 39 | (*pEnv)->CallVoidMethod(pEnv, pObj, sSaveFrameMID, pBitmap, filePath); 40 | LOGI("call java method to save frame %d done", iFrame); 41 | } 42 | 43 | jobject createBitmap(JNIEnv *pEnv, int pWidth, int pHeight) { 44 | int i; 45 | //get Bitmap class and createBitmap method ID 46 | jclass javaBitmapClass = (jclass)(*pEnv)->FindClass(pEnv, "android/graphics/Bitmap"); 47 | jmethodID mid = (*pEnv)->GetStaticMethodID(pEnv, javaBitmapClass, "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;"); 48 | //create Bitmap.Config 49 | //reference: https://forums.oracle.com/thread/1548728 50 | const wchar_t* configName = L"ARGB_8888"; 51 | int len = wcslen(configName); 52 | jstring jConfigName; 53 | if (sizeof(wchar_t) != sizeof(jchar)) { 54 | //wchar_t is defined as different length than jchar(2 bytes) 55 | jchar* str = (jchar*)malloc((len+1)*sizeof(jchar)); 56 | for (i = 0; i < len; ++i) { 57 | str[i] = (jchar)configName[i]; 58 | } 59 | str[len] = 0; 60 | jConfigName = (*pEnv)->NewString(pEnv, (const jchar*)str, len); 61 | } else { 62 | //wchar_t is defined same length as jchar(2 bytes) 63 | jConfigName = (*pEnv)->NewString(pEnv, (const jchar*)configName, len); 64 | } 65 | jclass bitmapConfigClass = (*pEnv)->FindClass(pEnv, "android/graphics/Bitmap$Config"); 66 | jobject javaBitmapConfig = (*pEnv)->CallStaticObjectMethod(pEnv, bitmapConfigClass, 67 | (*pEnv)->GetStaticMethodID(pEnv, bitmapConfigClass, "valueOf", "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;"), jConfigName); 68 | //create the bitmap 69 | return (*pEnv)->CallStaticObjectMethod(pEnv, javaBitmapClass, mid, pWidth, pHeight, javaBitmapConfig); 70 | } 71 | 72 | jint naMain(JNIEnv *pEnv, jobject pObj, jobject pMainAct, jstring pFileName, jint pNumOfFrames) { 73 | AVFormatContext *pFormatCtx = NULL; 74 | int i, videoStream; 75 | AVCodecContext *pCodecCtx = NULL; 76 | AVCodec *pCodec = NULL; 77 | AVFrame *pFrame = NULL; 78 | AVFrame *pFrameRGBA = NULL; 79 | AVPacket packet; 80 | int frameFinished; 81 | jobject bitmap; 82 | void* buffer; 83 | 84 | AVDictionary *optionsDict = NULL; 85 | struct SwsContext *sws_ctx = NULL; 86 | char *videoFileName; 87 | 88 | // Register all formats and codecs 89 | av_register_all(); 90 | 91 | //get C string from JNI jstring 92 | videoFileName = (char *)(*pEnv)->GetStringUTFChars(pEnv, pFileName, NULL); 93 | 94 | // Open video file 95 | if(avformat_open_input(&pFormatCtx, videoFileName, NULL, NULL)!=0) 96 | return -1; // Couldn't open file 97 | 98 | // Retrieve stream information 99 | if(avformat_find_stream_info(pFormatCtx, NULL)<0) 100 | return -1; // Couldn't find stream information 101 | 102 | // Dump information about file onto standard error 103 | av_dump_format(pFormatCtx, 0, videoFileName, 0); 104 | 105 | // Find the first video stream 106 | videoStream=-1; 107 | for(i=0; inb_streams; i++) { 108 | if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) { 109 | videoStream=i; 110 | break; 111 | } 112 | } 113 | if(videoStream==-1) 114 | return -1; // Didn't find a video stream 115 | 116 | // Get a pointer to the codec context for the video stream 117 | pCodecCtx=pFormatCtx->streams[videoStream]->codec; 118 | 119 | // Find the decoder for the video stream 120 | pCodec=avcodec_find_decoder(pCodecCtx->codec_id); 121 | if(pCodec==NULL) { 122 | fprintf(stderr, "Unsupported codec!\n"); 123 | return -1; // Codec not found 124 | } 125 | // Open codec 126 | if(avcodec_open2(pCodecCtx, pCodec, &optionsDict)<0) 127 | return -1; // Could not open codec 128 | 129 | // Allocate video frame 130 | pFrame=avcodec_alloc_frame(); 131 | 132 | // Allocate an AVFrame structure 133 | pFrameRGBA=avcodec_alloc_frame(); 134 | if(pFrameRGBA==NULL) 135 | return -1; 136 | 137 | //create a bitmap as the buffer for pFrameRGBA 138 | bitmap = createBitmap(pEnv, pCodecCtx->width, pCodecCtx->height); 139 | if (AndroidBitmap_lockPixels(pEnv, bitmap, &buffer) < 0) 140 | return -1; 141 | //get the scaling context 142 | sws_ctx = sws_getContext 143 | ( 144 | pCodecCtx->width, 145 | pCodecCtx->height, 146 | pCodecCtx->pix_fmt, 147 | pCodecCtx->width, 148 | pCodecCtx->height, 149 | AV_PIX_FMT_RGBA, 150 | SWS_BILINEAR, 151 | NULL, 152 | NULL, 153 | NULL 154 | ); 155 | 156 | // Assign appropriate parts of bitmap to image planes in pFrameRGBA 157 | // Note that pFrameRGBA is an AVFrame, but AVFrame is a superset 158 | // of AVPicture 159 | avpicture_fill((AVPicture *)pFrameRGBA, buffer, AV_PIX_FMT_RGBA, 160 | pCodecCtx->width, pCodecCtx->height); 161 | 162 | // Read frames and save first five frames to disk 163 | i=0; 164 | while(av_read_frame(pFormatCtx, &packet)>=0) { 165 | // Is this a packet from the video stream? 166 | if(packet.stream_index==videoStream) { 167 | // Decode video frame 168 | avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, 169 | &packet); 170 | // Did we get a video frame? 171 | if(frameFinished) { 172 | // Convert the image from its native format to RGBA 173 | sws_scale 174 | ( 175 | sws_ctx, 176 | (uint8_t const * const *)pFrame->data, 177 | pFrame->linesize, 178 | 0, 179 | pCodecCtx->height, 180 | pFrameRGBA->data, 181 | pFrameRGBA->linesize 182 | ); 183 | 184 | // Save the frame to disk 185 | if(++i<=pNumOfFrames) { 186 | SaveFrame(pEnv, pMainAct, bitmap, pCodecCtx->width, pCodecCtx->height, i); 187 | LOGI("save frame %d", i); 188 | } 189 | } 190 | } 191 | // Free the packet that was allocated by av_read_frame 192 | av_free_packet(&packet); 193 | } 194 | 195 | //unlock the bitmap 196 | AndroidBitmap_unlockPixels(pEnv, bitmap); 197 | 198 | // Free the RGB image 199 | av_free(pFrameRGBA); 200 | 201 | // Free the YUV frame 202 | av_free(pFrame); 203 | 204 | // Close the codec 205 | avcodec_close(pCodecCtx); 206 | 207 | // Close the video file 208 | avformat_close_input(&pFormatCtx); 209 | 210 | return 0; 211 | } 212 | 213 | jint JNI_OnLoad(JavaVM* pVm, void* reserved) { 214 | JNIEnv* env; 215 | if ((*pVm)->GetEnv(pVm, (void **)&env, JNI_VERSION_1_6) != JNI_OK) { 216 | return -1; 217 | } 218 | JNINativeMethod nm[1]; 219 | nm[0].name = "naMain"; 220 | nm[0].signature = "(Lroman10/tutorial/android_ffmpeg_tutorial01/MainActivity;Ljava/lang/String;I)I"; 221 | nm[0].fnPtr = (void*)naMain; 222 | jclass cls = (*env)->FindClass(env, "roman10/tutorial/android_ffmpeg_tutorial01/MainActivity"); 223 | //Register methods with env->RegisterNatives. 224 | (*env)->RegisterNatives(env, cls, nm, 1); 225 | return JNI_VERSION_1_6; 226 | } 227 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/libs/android-support-v4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman10/android-ffmpeg-tutorial/fc85bd0dc7971664139ee3e8d03601d6450dc2e0/android-ffmpeg-tutorial01/libs/android-support-v4.jar -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/libs/armeabi/libavcodec-55.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman10/android-ffmpeg-tutorial/fc85bd0dc7971664139ee3e8d03601d6450dc2e0/android-ffmpeg-tutorial01/libs/armeabi/libavcodec-55.so -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/libs/armeabi/libavformat-55.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman10/android-ffmpeg-tutorial/fc85bd0dc7971664139ee3e8d03601d6450dc2e0/android-ffmpeg-tutorial01/libs/armeabi/libavformat-55.so -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/libs/armeabi/libavutil-52.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman10/android-ffmpeg-tutorial/fc85bd0dc7971664139ee3e8d03601d6450dc2e0/android-ffmpeg-tutorial01/libs/armeabi/libavutil-52.so -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/libs/armeabi/libswscale-2.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman10/android-ffmpeg-tutorial/fc85bd0dc7971664139ee3e8d03601d6450dc2e0/android-ffmpeg-tutorial01/libs/armeabi/libswscale-2.so -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/libs/armeabi/libtutorial01.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman10/android-ffmpeg-tutorial/fc85bd0dc7971664139ee3e8d03601d6450dc2e0/android-ffmpeg-tutorial01/libs/armeabi/libtutorial01.so -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/proguard-project.txt: -------------------------------------------------------------------------------- 1 | # To enable ProGuard in your project, edit project.properties 2 | # to define the proguard.config property as described in that file. 3 | # 4 | # Add project specific ProGuard rules here. 5 | # By default, the flags in this file are appended to flags specified 6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt 7 | # You can edit the include path and order by changing the ProGuard 8 | # include property in project.properties. 9 | # 10 | # For more details, see 11 | # http://developer.android.com/guide/developing/tools/proguard.html 12 | 13 | # Add any project specific keep options here: 14 | 15 | # If your project uses WebView with JS, uncomment the following 16 | # and specify the fully qualified class name to the JavaScript interface 17 | # class: 18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 19 | # public *; 20 | #} 21 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-18 15 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman10/android-ffmpeg-tutorial/fc85bd0dc7971664139ee3e8d03601d6450dc2e0/android-ffmpeg-tutorial01/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman10/android-ffmpeg-tutorial/fc85bd0dc7971664139ee3e8d03601d6450dc2e0/android-ffmpeg-tutorial01/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman10/android-ffmpeg-tutorial/fc85bd0dc7971664139ee3e8d03601d6450dc2e0/android-ffmpeg-tutorial01/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman10/android-ffmpeg-tutorial/fc85bd0dc7971664139ee3e8d03601d6450dc2e0/android-ffmpeg-tutorial01/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 19 | 26 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/res/values-sw600dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/res/values-sw720dp-land/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 128dp 8 | 9 | 10 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/res/values-v11/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/res/values-v14/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/res/values/attrs_my_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16dp 5 | 16dp 6 | 7 | 8 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | android-ffmpeg-tutorial 5 | 6 | 7 | No. of frames to grab 8 | Start 9 | 10 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 14 | 15 | 16 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/src/roman10/tutorial/android_ffmpeg_tutorial01/MainActivity.java: -------------------------------------------------------------------------------- 1 | package roman10.tutorial.android_ffmpeg_tutorial01; 2 | 3 | import java.io.BufferedOutputStream; 4 | import java.io.File; 5 | import java.io.FileNotFoundException; 6 | import java.io.FileOutputStream; 7 | import java.io.IOException; 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | import java.util.List; 11 | 12 | import android.os.AsyncTask; 13 | import android.os.Bundle; 14 | import android.os.Environment; 15 | import android.app.Activity; 16 | import android.app.ProgressDialog; 17 | import android.content.Context; 18 | import android.graphics.Bitmap; 19 | import android.graphics.Bitmap.CompressFormat; 20 | import android.graphics.drawable.Drawable; 21 | import android.support.v4.view.PagerAdapter; 22 | import android.support.v4.view.ViewPager; 23 | import android.view.View; 24 | import android.view.ViewGroup.LayoutParams; 25 | import android.widget.Button; 26 | import android.widget.EditText; 27 | import android.widget.ImageView; 28 | import android.widget.ImageView.ScaleType; 29 | 30 | public class MainActivity extends Activity { 31 | private static final String FRAME_DUMP_FOLDER_PATH = Environment.getExternalStorageDirectory() 32 | + File.separator + "android-ffmpeg-tutorial01"; 33 | private EditText mEditNumOfFrames; 34 | private ViewPager mViewPagerFrames; 35 | 36 | @Override 37 | protected void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | setContentView(R.layout.activity_main); 40 | //create directory for the tutorial 41 | File dumpFolder = new File(FRAME_DUMP_FOLDER_PATH); 42 | if (!dumpFolder.exists()) { 43 | dumpFolder.mkdirs(); 44 | } 45 | //copy input video file from assets folder to directory 46 | Utils.copyAssets(this, "1.mp4", FRAME_DUMP_FOLDER_PATH); 47 | mEditNumOfFrames = (EditText) this.findViewById(R.id.editNumOfFrames); 48 | mViewPagerFrames = (ViewPager) this.findViewById(R.id.viewPagerFrames); 49 | Button btnStart = (Button) this.findViewById(R.id.buttonStart); 50 | btnStart.setOnClickListener(new View.OnClickListener() { 51 | @Override 52 | public void onClick(View v) { 53 | //get number of frames to grab 54 | String numText = mEditNumOfFrames.getText().toString(); 55 | int numOfFrames; 56 | try { 57 | numOfFrames = Integer.valueOf(numText); 58 | } catch (Exception e) { 59 | e.printStackTrace(); 60 | numOfFrames = 5; 61 | } 62 | //limit number of frames to grab to 20 63 | if (numOfFrames > 20) { 64 | numOfFrames = 20; 65 | } 66 | //start processing using asynctask 67 | DumpFrameTask task = new DumpFrameTask(MainActivity.this, numOfFrames); 68 | task.execute(); 69 | } 70 | }); 71 | } 72 | 73 | private void displayDumpedFrames(){ 74 | //populate the view pager 75 | if (null != mViewPagerFrames) { 76 | VideoFrameAdapter adapter = new VideoFrameAdapter(MainActivity.this, 77 | FRAME_DUMP_FOLDER_PATH); 78 | mViewPagerFrames.setAdapter(adapter); 79 | } 80 | } 81 | 82 | private static class DumpFrameTask extends AsyncTask { 83 | int mlNumOfFrames; 84 | ProgressDialog mlDialog; 85 | MainActivity mlOuterAct; 86 | DumpFrameTask(MainActivity pContext, int pNumOfFrames) { 87 | mlNumOfFrames = pNumOfFrames; 88 | mlOuterAct = pContext; 89 | } 90 | @Override 91 | protected void onPreExecute() { 92 | mlDialog = ProgressDialog.show(mlOuterAct, "Dump Frames","Processing..Wait.." , false); 93 | } 94 | @Override 95 | protected Void doInBackground(Void... params) { 96 | naMain(mlOuterAct, FRAME_DUMP_FOLDER_PATH + File.separator + "1.mp4", mlNumOfFrames); 97 | return null; 98 | } 99 | @Override 100 | protected void onPostExecute(Void param) { 101 | if (null != mlDialog && mlDialog.isShowing()) { 102 | mlDialog.dismiss(); 103 | } 104 | mlOuterAct.displayDumpedFrames(); 105 | } 106 | } 107 | 108 | private void saveFrameToPath(Bitmap bitmap, String pPath) { 109 | int BUFFER_SIZE = 1024 * 8; 110 | try { 111 | File file = new File(pPath); 112 | file.createNewFile(); 113 | FileOutputStream fos = new FileOutputStream(file); 114 | final BufferedOutputStream bos = new BufferedOutputStream(fos, BUFFER_SIZE); 115 | bitmap.compress(CompressFormat.JPEG, 100, bos); 116 | bos.flush(); 117 | bos.close(); 118 | fos.close(); 119 | } catch (FileNotFoundException e) { 120 | e.printStackTrace(); 121 | } catch (IOException e) { 122 | e.printStackTrace(); 123 | } 124 | } 125 | 126 | 127 | private static class VideoFrameAdapter extends PagerAdapter { 128 | List mlFramePaths; 129 | MainActivity mlAct; 130 | VideoFrameAdapter(MainActivity pAct, String pFrameFolderPath) { 131 | mlAct = pAct; 132 | File frameFolder = new File(pFrameFolderPath); 133 | File[] framePaths = frameFolder.listFiles(); 134 | mlFramePaths = new ArrayList(); 135 | for (File aFile : framePaths) { 136 | if (aFile.getAbsolutePath().endsWith(".jpg")) { 137 | mlFramePaths.add(aFile.getAbsolutePath()); 138 | } 139 | } 140 | } 141 | public Object instantiateItem(View collection, int position) { 142 | ImageView view = new ImageView(mlAct); 143 | view.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, 144 | LayoutParams.FILL_PARENT)); 145 | view.setScaleType(ScaleType.CENTER_INSIDE); 146 | view.setImageDrawable(Drawable.createFromPath(mlFramePaths.get(position))); 147 | ((ViewPager) collection).addView(view); 148 | return view; 149 | } 150 | 151 | @Override 152 | public void destroyItem(View arg0, int arg1, Object arg2) { 153 | ((ViewPager) arg0).removeView((View) arg2); 154 | } 155 | 156 | @Override 157 | public int getCount() { 158 | if (null != mlFramePaths) { 159 | return mlFramePaths.size(); 160 | } else { 161 | return 0; 162 | } 163 | } 164 | 165 | @Override 166 | public boolean isViewFromObject(View view, Object object) { 167 | return view == object; 168 | } 169 | } 170 | 171 | private static native int naMain(MainActivity pObject, String pVideoFileName, int pNumOfFrames); 172 | 173 | static { 174 | System.loadLibrary("avutil-52"); 175 | System.loadLibrary("avcodec-55"); 176 | System.loadLibrary("avformat-55"); 177 | System.loadLibrary("swscale-2"); 178 | System.loadLibrary("tutorial01"); 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial01/src/roman10/tutorial/android_ffmpeg_tutorial01/Utils.java: -------------------------------------------------------------------------------- 1 | package roman10.tutorial.android_ffmpeg_tutorial01; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.OutputStream; 8 | 9 | import android.content.Context; 10 | import android.content.res.AssetManager; 11 | import android.util.Log; 12 | 13 | public class Utils { 14 | public static void copyAssets(Context pContext, String pAssetFilePath, String pDestDirPath) { 15 | AssetManager assetManager = pContext.getAssets(); 16 | InputStream in = null; 17 | OutputStream out = null; 18 | try { 19 | in = assetManager.open(pAssetFilePath); 20 | File outFile = new File(pDestDirPath, pAssetFilePath); 21 | out = new FileOutputStream(outFile); 22 | copyFile(in, out); 23 | in.close(); 24 | in = null; 25 | out.flush(); 26 | out.close(); 27 | out = null; 28 | } catch(IOException e) { 29 | Log.e("tag", "Failed to copy asset file: " + pAssetFilePath, e); 30 | } 31 | } 32 | private static void copyFile(InputStream in, OutputStream out) throws IOException { 33 | byte[] buffer = new byte[1024*16]; 34 | int read; 35 | while((read = in.read(buffer)) != -1){ 36 | out.write(buffer, 0, read); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | android-ffmpeg-tutorial01 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.cdt.managedbuilder.core.genmakebuilder 10 | clean,full,incremental, 11 | 12 | 13 | ?children? 14 | ?name?=outputEntries\|?children?=?name?=entry\\\\\\\|\\\|?name?=entry\\\\\\\|\\\|\|| 15 | 16 | 17 | ?name? 18 | 19 | 20 | 21 | org.eclipse.cdt.make.core.append_environment 22 | true 23 | 24 | 25 | org.eclipse.cdt.make.core.buildArguments 26 | 27 | 28 | 29 | org.eclipse.cdt.make.core.buildCommand 30 | ndk-build 31 | 32 | 33 | org.eclipse.cdt.make.core.cleanBuildTarget 34 | clean 35 | 36 | 37 | org.eclipse.cdt.make.core.contents 38 | org.eclipse.cdt.make.core.activeConfigSettings 39 | 40 | 41 | org.eclipse.cdt.make.core.enableAutoBuild 42 | false 43 | 44 | 45 | org.eclipse.cdt.make.core.enableCleanBuild 46 | true 47 | 48 | 49 | org.eclipse.cdt.make.core.enableFullBuild 50 | true 51 | 52 | 53 | org.eclipse.cdt.make.core.stopOnError 54 | true 55 | 56 | 57 | org.eclipse.cdt.make.core.useDefaultBuildCmd 58 | true 59 | 60 | 61 | 62 | 63 | com.android.ide.eclipse.adt.ResourceManagerBuilder 64 | 65 | 66 | 67 | 68 | com.android.ide.eclipse.adt.PreCompilerBuilder 69 | 70 | 71 | 72 | 73 | org.eclipse.jdt.core.javabuilder 74 | 75 | 76 | 77 | 78 | com.android.ide.eclipse.adt.ApkBuilder 79 | 80 | 81 | 82 | 83 | org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder 84 | full,incremental, 85 | 86 | 87 | 88 | 89 | 90 | com.android.ide.eclipse.adt.AndroidNature 91 | org.eclipse.jdt.core.javanature 92 | org.eclipse.cdt.core.cnature 93 | org.eclipse.cdt.core.ccnature 94 | org.eclipse.cdt.managedbuilder.core.managedBuildNature 95 | org.eclipse.cdt.managedbuilder.core.ScannerConfigNature 96 | 97 | 98 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 12 | 13 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/assets/1.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman10/android-ffmpeg-tutorial/fc85bd0dc7971664139ee3e8d03601d6450dc2e0/android-ffmpeg-tutorial02/assets/1.mp4 -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/assets/12.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman10/android-ffmpeg-tutorial/fc85bd0dc7971664139ee3e8d03601d6450dc2e0/android-ffmpeg-tutorial02/assets/12.mp4 -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | 5 | LOCAL_MODULE := tutorial02 6 | LOCAL_SRC_FILES := tutorial02.c 7 | LOCAL_LDLIBS := -llog -ljnigraphics -lz -landroid 8 | LOCAL_SHARED_LIBRARIES := libavformat libavcodec libswscale libavutil 9 | 10 | include $(BUILD_SHARED_LIBRARY) 11 | $(call import-module,ffmpeg-2.0.1/android/arm) 12 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := armeabi 2 | #APP_ABI := armeabi-v7a 3 | APP_PLATFORM := android-10 4 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/jni/tutorial02.c: -------------------------------------------------------------------------------- 1 | // tutorial02.c 2 | // A pedagogical video player that will stream through every video frame as fast as it can. 3 | // 4 | // This tutorial was written by Stephen Dranger (dranger@gmail.com). 5 | // 6 | // Code based on FFplay, Copyright (c) 2003 Fabrice Bellard, 7 | // and a tutorial by Martin Bohme (boehme@inb.uni-luebeckREMOVETHIS.de) 8 | // Tested on Gentoo, CVS version 5/01/07 compiled with GCC 4.1.1 9 | // 10 | // The code is modified so that it can be compiled to a shared library and run on Android 11 | // 12 | // The code play the video stream on your screen 13 | // 14 | // Feipeng Liu (http://www.roman10.net/) 15 | // Aug 2013 16 | 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #define LOG_TAG "android-ffmpeg-tutorial02" 31 | #define LOGI(...) __android_log_print(4, LOG_TAG, __VA_ARGS__); 32 | #define LOGE(...) __android_log_print(6, LOG_TAG, __VA_ARGS__); 33 | 34 | ANativeWindow* window; 35 | char *videoFileName; 36 | AVFormatContext *formatCtx = NULL; 37 | int videoStream; 38 | AVCodecContext *codecCtx = NULL; 39 | AVFrame *decodedFrame = NULL; 40 | AVFrame *frameRGBA = NULL; 41 | jobject bitmap; 42 | void* buffer; 43 | struct SwsContext *sws_ctx = NULL; 44 | int width; 45 | int height; 46 | int stop; 47 | 48 | jint naInit(JNIEnv *pEnv, jobject pObj, jstring pFileName) { 49 | AVCodec *pCodec = NULL; 50 | int i; 51 | AVDictionary *optionsDict = NULL; 52 | 53 | videoFileName = (char *)(*pEnv)->GetStringUTFChars(pEnv, pFileName, NULL); 54 | LOGI("video file name is %s", videoFileName); 55 | // Register all formats and codecs 56 | av_register_all(); 57 | // Open video file 58 | if(avformat_open_input(&formatCtx, videoFileName, NULL, NULL)!=0) 59 | return -1; // Couldn't open file 60 | // Retrieve stream information 61 | if(avformat_find_stream_info(formatCtx, NULL)<0) 62 | return -1; // Couldn't find stream information 63 | // Dump information about file onto standard error 64 | av_dump_format(formatCtx, 0, videoFileName, 0); 65 | // Find the first video stream 66 | videoStream=-1; 67 | for(i=0; inb_streams; i++) { 68 | if(formatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) { 69 | videoStream=i; 70 | break; 71 | } 72 | } 73 | if(videoStream==-1) 74 | return -1; // Didn't find a video stream 75 | // Get a pointer to the codec context for the video stream 76 | codecCtx=formatCtx->streams[videoStream]->codec; 77 | // Find the decoder for the video stream 78 | pCodec=avcodec_find_decoder(codecCtx->codec_id); 79 | if(pCodec==NULL) { 80 | fprintf(stderr, "Unsupported codec!\n"); 81 | return -1; // Codec not found 82 | } 83 | // Open codec 84 | if(avcodec_open2(codecCtx, pCodec, &optionsDict)<0) 85 | return -1; // Could not open codec 86 | // Allocate video frame 87 | decodedFrame=avcodec_alloc_frame(); 88 | // Allocate an AVFrame structure 89 | frameRGBA=avcodec_alloc_frame(); 90 | if(frameRGBA==NULL) 91 | return -1; 92 | return 0; 93 | } 94 | 95 | jobject createBitmap(JNIEnv *pEnv, int pWidth, int pHeight) { 96 | int i; 97 | //get Bitmap class and createBitmap method ID 98 | jclass javaBitmapClass = (jclass)(*pEnv)->FindClass(pEnv, "android/graphics/Bitmap"); 99 | jmethodID mid = (*pEnv)->GetStaticMethodID(pEnv, javaBitmapClass, "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;"); 100 | //create Bitmap.Config 101 | //reference: https://forums.oracle.com/thread/1548728 102 | const wchar_t* configName = L"ARGB_8888"; 103 | int len = wcslen(configName); 104 | jstring jConfigName; 105 | if (sizeof(wchar_t) != sizeof(jchar)) { 106 | //wchar_t is defined as different length than jchar(2 bytes) 107 | jchar* str = (jchar*)malloc((len+1)*sizeof(jchar)); 108 | for (i = 0; i < len; ++i) { 109 | str[i] = (jchar)configName[i]; 110 | } 111 | str[len] = 0; 112 | jConfigName = (*pEnv)->NewString(pEnv, (const jchar*)str, len); 113 | } else { 114 | //wchar_t is defined same length as jchar(2 bytes) 115 | jConfigName = (*pEnv)->NewString(pEnv, (const jchar*)configName, len); 116 | } 117 | jclass bitmapConfigClass = (*pEnv)->FindClass(pEnv, "android/graphics/Bitmap$Config"); 118 | jobject javaBitmapConfig = (*pEnv)->CallStaticObjectMethod(pEnv, bitmapConfigClass, 119 | (*pEnv)->GetStaticMethodID(pEnv, bitmapConfigClass, "valueOf", "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;"), jConfigName); 120 | //create the bitmap 121 | return (*pEnv)->CallStaticObjectMethod(pEnv, javaBitmapClass, mid, pWidth, pHeight, javaBitmapConfig); 122 | } 123 | 124 | jintArray naGetVideoRes(JNIEnv *pEnv, jobject pObj) { 125 | jintArray lRes; 126 | if (NULL == codecCtx) { 127 | return NULL; 128 | } 129 | lRes = (*pEnv)->NewIntArray(pEnv, 2); 130 | if (lRes == NULL) { 131 | LOGI(1, "cannot allocate memory for video size"); 132 | return NULL; 133 | } 134 | jint lVideoRes[2]; 135 | lVideoRes[0] = codecCtx->width; 136 | lVideoRes[1] = codecCtx->height; 137 | (*pEnv)->SetIntArrayRegion(pEnv, lRes, 0, 2, lVideoRes); 138 | return lRes; 139 | } 140 | 141 | void naSetSurface(JNIEnv *pEnv, jobject pObj, jobject pSurface) { 142 | if (0 != pSurface) { 143 | // get the native window reference 144 | window = ANativeWindow_fromSurface(pEnv, pSurface); 145 | // set format and size of window buffer 146 | ANativeWindow_setBuffersGeometry(window, 0, 0, WINDOW_FORMAT_RGBA_8888); 147 | } else { 148 | // release the native window 149 | ANativeWindow_release(window); 150 | } 151 | } 152 | 153 | jint naSetup(JNIEnv *pEnv, jobject pObj, int pWidth, int pHeight) { 154 | width = pWidth; 155 | height = pHeight; 156 | //create a bitmap as the buffer for frameRGBA 157 | bitmap = createBitmap(pEnv, pWidth, pHeight); 158 | if (AndroidBitmap_lockPixels(pEnv, bitmap, &buffer) < 0) 159 | return -1; 160 | //get the scaling context 161 | sws_ctx = sws_getContext ( 162 | codecCtx->width, 163 | codecCtx->height, 164 | codecCtx->pix_fmt, 165 | pWidth, 166 | pHeight, 167 | AV_PIX_FMT_RGBA, 168 | SWS_BILINEAR, 169 | NULL, 170 | NULL, 171 | NULL 172 | ); 173 | // Assign appropriate parts of bitmap to image planes in pFrameRGBA 174 | // Note that pFrameRGBA is an AVFrame, but AVFrame is a superset 175 | // of AVPicture 176 | avpicture_fill((AVPicture *)frameRGBA, buffer, AV_PIX_FMT_RGBA, 177 | pWidth, pHeight); 178 | return 0; 179 | } 180 | 181 | void finish(JNIEnv *pEnv) { 182 | //unlock the bitmap 183 | AndroidBitmap_unlockPixels(pEnv, bitmap); 184 | av_free(buffer); 185 | // Free the RGB image 186 | av_free(frameRGBA); 187 | // Free the YUV frame 188 | av_free(decodedFrame); 189 | // Close the codec 190 | avcodec_close(codecCtx); 191 | // Close the video file 192 | avformat_close_input(&formatCtx); 193 | } 194 | 195 | void decodeAndRender(JNIEnv *pEnv) { 196 | ANativeWindow_Buffer windowBuffer; 197 | AVPacket packet; 198 | int i=0; 199 | int frameFinished; 200 | int lineCnt; 201 | while(av_read_frame(formatCtx, &packet)>=0 && !stop) { 202 | // Is this a packet from the video stream? 203 | if(packet.stream_index==videoStream) { 204 | // Decode video frame 205 | avcodec_decode_video2(codecCtx, decodedFrame, &frameFinished, 206 | &packet); 207 | // Did we get a video frame? 208 | if(frameFinished) { 209 | // Convert the image from its native format to RGBA 210 | sws_scale 211 | ( 212 | sws_ctx, 213 | (uint8_t const * const *)decodedFrame->data, 214 | decodedFrame->linesize, 215 | 0, 216 | codecCtx->height, 217 | frameRGBA->data, 218 | frameRGBA->linesize 219 | ); 220 | // lock the window buffer 221 | if (ANativeWindow_lock(window, &windowBuffer, NULL) < 0) { 222 | LOGE("cannot lock window"); 223 | } else { 224 | // draw the frame on buffer 225 | LOGI("copy buffer %d:%d:%d", width, height, width*height*4); 226 | LOGI("window buffer: %d:%d:%d", windowBuffer.width, 227 | windowBuffer.height, windowBuffer.stride); 228 | memcpy(windowBuffer.bits, buffer, width * height * 4); 229 | // unlock the window buffer and post it to display 230 | ANativeWindow_unlockAndPost(window); 231 | // count number of frames 232 | ++i; 233 | } 234 | } 235 | } 236 | // Free the packet that was allocated by av_read_frame 237 | av_free_packet(&packet); 238 | } 239 | LOGI("total No. of frames decoded and rendered %d", i); 240 | finish(pEnv); 241 | } 242 | 243 | /** 244 | * start the video playback 245 | */ 246 | void naPlay(JNIEnv *pEnv, jobject pObj) { 247 | //create a new thread for video decode and render 248 | pthread_t decodeThread; 249 | stop = 0; 250 | pthread_create(&decodeThread, NULL, decodeAndRender, NULL); 251 | } 252 | 253 | /** 254 | * stop the video playback 255 | */ 256 | void naStop(JNIEnv *pEnv, jobject pObj) { 257 | stop = 1; 258 | } 259 | 260 | jint JNI_OnLoad(JavaVM* pVm, void* reserved) { 261 | JNIEnv* env; 262 | if ((*pVm)->GetEnv(pVm, (void **)&env, JNI_VERSION_1_6) != JNI_OK) { 263 | return -1; 264 | } 265 | JNINativeMethod nm[8]; 266 | nm[0].name = "naInit"; 267 | nm[0].signature = "(Ljava/lang/String;)I"; 268 | nm[0].fnPtr = (void*)naInit; 269 | 270 | nm[1].name = "naSetSurface"; 271 | nm[1].signature = "(Landroid/view/Surface;)V"; 272 | nm[1].fnPtr = (void*)naSetSurface; 273 | 274 | nm[2].name = "naGetVideoRes"; 275 | nm[2].signature = "()[I"; 276 | nm[2].fnPtr = (void*)naGetVideoRes; 277 | 278 | nm[3].name = "naSetup"; 279 | nm[3].signature = "(II)I"; 280 | nm[3].fnPtr = (void*)naSetup; 281 | 282 | nm[4].name = "naPlay"; 283 | nm[4].signature = "()V"; 284 | nm[4].fnPtr = (void*)naPlay; 285 | 286 | nm[5].name = "naStop"; 287 | nm[5].signature = "()V"; 288 | nm[5].fnPtr = (void*)naStop; 289 | 290 | jclass cls = (*env)->FindClass(env, "roman10/tutorial/android_ffmpeg_tutorial02/MainActivity"); 291 | //Register methods with env->RegisterNatives. 292 | (*env)->RegisterNatives(env, cls, nm, 6); 293 | return JNI_VERSION_1_6; 294 | } 295 | 296 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/libs/android-support-v4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman10/android-ffmpeg-tutorial/fc85bd0dc7971664139ee3e8d03601d6450dc2e0/android-ffmpeg-tutorial02/libs/android-support-v4.jar -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/libs/armeabi/libavcodec-55.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman10/android-ffmpeg-tutorial/fc85bd0dc7971664139ee3e8d03601d6450dc2e0/android-ffmpeg-tutorial02/libs/armeabi/libavcodec-55.so -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/libs/armeabi/libavformat-55.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman10/android-ffmpeg-tutorial/fc85bd0dc7971664139ee3e8d03601d6450dc2e0/android-ffmpeg-tutorial02/libs/armeabi/libavformat-55.so -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/libs/armeabi/libavutil-52.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman10/android-ffmpeg-tutorial/fc85bd0dc7971664139ee3e8d03601d6450dc2e0/android-ffmpeg-tutorial02/libs/armeabi/libavutil-52.so -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/libs/armeabi/libswscale-2.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman10/android-ffmpeg-tutorial/fc85bd0dc7971664139ee3e8d03601d6450dc2e0/android-ffmpeg-tutorial02/libs/armeabi/libswscale-2.so -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/libs/armeabi/libtutorial02.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman10/android-ffmpeg-tutorial/fc85bd0dc7971664139ee3e8d03601d6450dc2e0/android-ffmpeg-tutorial02/libs/armeabi/libtutorial02.so -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/proguard-project.txt: -------------------------------------------------------------------------------- 1 | # To enable ProGuard in your project, edit project.properties 2 | # to define the proguard.config property as described in that file. 3 | # 4 | # Add project specific ProGuard rules here. 5 | # By default, the flags in this file are appended to flags specified 6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt 7 | # You can edit the include path and order by changing the ProGuard 8 | # include property in project.properties. 9 | # 10 | # For more details, see 11 | # http://developer.android.com/guide/developing/tools/proguard.html 12 | 13 | # Add any project specific keep options here: 14 | 15 | # If your project uses WebView with JS, uncomment the following 16 | # and specify the fully qualified class name to the JavaScript interface 17 | # class: 18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 19 | # public *; 20 | #} 21 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-18 15 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman10/android-ffmpeg-tutorial/fc85bd0dc7971664139ee3e8d03601d6450dc2e0/android-ffmpeg-tutorial02/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman10/android-ffmpeg-tutorial/fc85bd0dc7971664139ee3e8d03601d6450dc2e0/android-ffmpeg-tutorial02/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman10/android-ffmpeg-tutorial/fc85bd0dc7971664139ee3e8d03601d6450dc2e0/android-ffmpeg-tutorial02/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman10/android-ffmpeg-tutorial/fc85bd0dc7971664139ee3e8d03601d6450dc2e0/android-ffmpeg-tutorial02/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | 20 | 21 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/res/values-sw600dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/res/values-sw720dp-land/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 128dp 8 | 9 | 10 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/res/values-v11/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/res/values-v14/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/res/values/attrs_my_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16dp 5 | 16dp 6 | 7 | 8 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | android-ffmpeg-tutorial 5 | 6 | 7 | No. of frames to grab 8 | Start 9 | 10 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 14 | 15 | 16 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/src/roman10/tutorial/android_ffmpeg_tutorial02/MainActivity.java: -------------------------------------------------------------------------------- 1 | package roman10.tutorial.android_ffmpeg_tutorial02; 2 | 3 | import java.io.File; 4 | 5 | import android.os.Build; 6 | import android.os.Bundle; 7 | import android.os.Environment; 8 | import android.annotation.SuppressLint; 9 | import android.app.Activity; 10 | import android.graphics.Point; 11 | import android.util.Log; 12 | import android.view.Display; 13 | import android.view.Surface; 14 | import android.view.SurfaceHolder; 15 | import android.view.SurfaceView; 16 | import android.view.View; 17 | import android.view.Window; 18 | import android.view.WindowManager; 19 | import android.widget.Button; 20 | import android.widget.RelativeLayout; 21 | import android.widget.RelativeLayout.LayoutParams; 22 | 23 | public class MainActivity extends Activity implements SurfaceHolder.Callback { 24 | private static final String TAG = "android-ffmpeg-tutorial02"; 25 | private static final String FRAME_DUMP_FOLDER_PATH = Environment.getExternalStorageDirectory() 26 | + File.separator + "android-ffmpeg-tutorial02"; 27 | 28 | // video used to fill the width of the screen 29 | private static final String videoFileName = "1.mp4"; //640x360 30 | // video used to fill the height of the screen 31 | // private static final String videoFileName = "12.mp4"; //200x640 32 | 33 | private SurfaceView mSurfaceView; 34 | 35 | @Override 36 | protected void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | requestWindowFeature(Window.FEATURE_NO_TITLE); 39 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 40 | WindowManager.LayoutParams.FLAG_FULLSCREEN); 41 | setContentView(R.layout.activity_main); 42 | //create directory for the tutorial 43 | File dumpFolder = new File(FRAME_DUMP_FOLDER_PATH); 44 | if (!dumpFolder.exists()) { 45 | dumpFolder.mkdirs(); 46 | } 47 | //copy input video file from assets folder to directory 48 | Utils.copyAssets(this, videoFileName, FRAME_DUMP_FOLDER_PATH); 49 | 50 | mSurfaceView = (SurfaceView)findViewById(R.id.surfaceview); 51 | mSurfaceView.getHolder().addCallback(this); 52 | 53 | Button btnStart = (Button) this.findViewById(R.id.buttonStart); 54 | btnStart.setOnClickListener(new View.OnClickListener() { 55 | @Override 56 | public void onClick(View v) { 57 | naInit(FRAME_DUMP_FOLDER_PATH + File.separator + videoFileName); 58 | int[] res = naGetVideoRes(); 59 | Log.d(TAG, "res width " + res[0] + ": height " + res[1]); 60 | int[] screenRes = getScreenRes(); 61 | int width, height; 62 | float widthScaledRatio = screenRes[0]*1.0f/res[0]; 63 | float heightScaledRatio = screenRes[1]*1.0f/res[1]; 64 | if (widthScaledRatio > heightScaledRatio) { 65 | //use heightScaledRatio 66 | width = (int) (res[0]*heightScaledRatio); 67 | height = screenRes[1]; 68 | } else { 69 | //use widthScaledRatio 70 | width = screenRes[0]; 71 | height = (int) (res[1]*widthScaledRatio); 72 | } 73 | Log.d(TAG, "width " + width + ",height:" + height); 74 | updateSurfaceView(width, height); 75 | naSetup(width, height); 76 | naPlay(); 77 | } 78 | }); 79 | } 80 | 81 | private void updateSurfaceView(int pWidth, int pHeight) { 82 | //update surfaceview dimension, this will cause the native window to change 83 | RelativeLayout.LayoutParams params = (LayoutParams) mSurfaceView.getLayoutParams(); 84 | params.width = pWidth; 85 | params.height = pHeight; 86 | mSurfaceView.setLayoutParams(params); 87 | } 88 | 89 | @SuppressLint("NewApi") 90 | private int[] getScreenRes() { 91 | int[] res = new int[2]; 92 | Display display = getWindowManager().getDefaultDisplay(); 93 | if (Build.VERSION.SDK_INT >= 13) { 94 | Point size = new Point(); 95 | display.getSize(size); 96 | res[0] = size.x; 97 | res[1] = size.y; 98 | } else { 99 | res[0] = display.getWidth(); // deprecated 100 | res[1] = display.getHeight(); // deprecated 101 | } 102 | return res; 103 | } 104 | 105 | @Override 106 | public void onDestroy() { 107 | super.onDestroy(); 108 | naStop(); 109 | } 110 | 111 | @Override 112 | public void surfaceChanged(SurfaceHolder holder, int format, int width, 113 | int height) { 114 | Log.d(TAG, "surfacechanged: " + width + ":" + height); 115 | naSetSurface(holder.getSurface()); 116 | } 117 | 118 | @Override 119 | public void surfaceCreated(SurfaceHolder holder) { 120 | 121 | } 122 | 123 | @Override 124 | public void surfaceDestroyed(SurfaceHolder holder) { 125 | Log.d(TAG, "surfaceDestroyed"); 126 | naSetSurface(null); 127 | } 128 | 129 | private static native int naInit(String pFileName); 130 | private static native int[] naGetVideoRes(); 131 | private static native void naSetSurface(Surface pSurface); 132 | private static native int naSetup(int pWidth, int pHeight); 133 | private static native void naPlay(); 134 | private static native void naStop(); 135 | 136 | static { 137 | System.loadLibrary("avutil-52"); 138 | System.loadLibrary("avcodec-55"); 139 | System.loadLibrary("avformat-55"); 140 | System.loadLibrary("swscale-2"); 141 | System.loadLibrary("tutorial02"); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /android-ffmpeg-tutorial02/src/roman10/tutorial/android_ffmpeg_tutorial02/Utils.java: -------------------------------------------------------------------------------- 1 | package roman10.tutorial.android_ffmpeg_tutorial02; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.OutputStream; 8 | 9 | import android.content.Context; 10 | import android.content.res.AssetManager; 11 | import android.util.Log; 12 | 13 | public class Utils { 14 | public static void copyAssets(Context pContext, String pAssetFilePath, String pDestDirPath) { 15 | AssetManager assetManager = pContext.getAssets(); 16 | InputStream in = null; 17 | OutputStream out = null; 18 | try { 19 | in = assetManager.open(pAssetFilePath); 20 | File outFile = new File(pDestDirPath, pAssetFilePath); 21 | out = new FileOutputStream(outFile); 22 | copyFile(in, out); 23 | in.close(); 24 | in = null; 25 | out.flush(); 26 | out.close(); 27 | out = null; 28 | } catch(IOException e) { 29 | Log.e("tag", "Failed to copy asset file: " + pAssetFilePath, e); 30 | } 31 | } 32 | private static void copyFile(InputStream in, OutputStream out) throws IOException { 33 | byte[] buffer = new byte[1024*16]; 34 | int read; 35 | while((read = in.read(buffer)) != -1){ 36 | out.write(buffer, 0, read); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /readme: -------------------------------------------------------------------------------- 1 | android-ffmpeg-tutorial 2 | 3 | This project contains 7 tutorials about how to write a video player on Android using ffmpeg. Each of the tutorial corresponds to one tutorial in https://github.com/chelyaev/ffmpeg-tutorial 4 | 5 | MUST READ: 6 | Before start coding the player, we will need to compile ffmpeg libraries for Android, please follow the instructions here: 7 | http://www.roman10.net/how-to-build-ffmpeg-with-ndk-r9/ 8 | 9 | I'll update the project whenever a new tutorial is completed 10 | 11 | --------------------------------------------------------------------------------