├── 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 |
--------------------------------------------------------------------------------