Skip to content

Commit be00040

Browse files
committed
add android cts ensured format strategy, use custom exception
1 parent d7b2a89 commit be00040

File tree

9 files changed

+258
-29
lines changed

9 files changed

+258
-29
lines changed

example/src/main/java/net/ypresto/androidtranscoder/example/TranscoderActivity.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import android.net.Uri;
77
import android.os.Bundle;
88
import android.os.ParcelFileDescriptor;
9+
import android.os.SystemClock;
910
import android.util.Log;
1011
import android.view.Menu;
1112
import android.view.MenuItem;
@@ -23,6 +24,7 @@
2324

2425

2526
public class TranscoderActivity extends Activity {
27+
private static final String TAG = "TranscoderActivity";
2628
private static final int REQUEST_CODE_PICK = 1;
2729

2830
@Override
@@ -44,7 +46,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
4446
final File file;
4547
if (resultCode == RESULT_OK) {
4648
try {
47-
file = File.createTempFile("transcode_test_", ".mp4", getExternalCacheDir());
49+
file = File.createTempFile("transcode_test", ".mp4", getExternalFilesDir(null));
4850
} catch (IOException e) {
4951
Toast.makeText(this, "Failed to create temporary file.", Toast.LENGTH_LONG).show();
5052
return;
@@ -61,6 +63,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
6163
final FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
6264
final ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress_bar);
6365
progressBar.setMax(1000);
66+
final long startTime = SystemClock.uptimeMillis();
6467
MediaTranscoder.Listener listener = new MediaTranscoder.Listener() {
6568
@Override
6669
public void onTranscodeProgress(double progress) {
@@ -74,6 +77,8 @@ public void onTranscodeProgress(double progress) {
7477

7578
@Override
7679
public void onTranscodeCompleted() {
80+
Log.d(TAG, "transcoding took " + (SystemClock.uptimeMillis() - startTime) + "ms");
81+
Toast.makeText(TranscoderActivity.this, "transcoded file placed on " + file, Toast.LENGTH_LONG).show();
7782
findViewById(R.id.select_video_button).setEnabled(true);
7883
progressBar.setIndeterminate(false);
7984
progressBar.setProgress(1000);
@@ -98,8 +103,9 @@ public void onTranscodeFailed(Exception exception) {
98103
}
99104
}
100105
};
106+
Log.d(TAG, "transcoding into " + file);
101107
MediaTranscoder.getInstance().transcodeVideo(fileDescriptor, file.getAbsolutePath(),
102-
MediaFormatStrategyPresets.EXPORT_PRESET_960x540, listener);
108+
MediaFormatStrategyPresets.createAndroid720pStrategy(), listener);
103109
findViewById(R.id.select_video_button).setEnabled(false);
104110
}
105111
break;

lib/build.gradle

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ buildscript {
33
jcenter()
44
}
55
dependencies {
6-
classpath 'com.novoda:android-bintray-release:0.2.3'
6+
classpath 'com.novoda:bintray-release:0.2.4'
77
}
88
}
99

1010
apply plugin: 'com.android.library'
11-
apply plugin: 'android-bintray-release'
11+
apply plugin: 'bintray-release'
1212

1313
android {
1414
compileSdkVersion 19
@@ -32,7 +32,7 @@ android {
3232
publish {
3333
groupId = 'net.ypresto.androidtranscoder'
3434
artifactId = 'android-transcoder'
35-
version = '0.1.3'
35+
version = '0.1.4-SNAPSHOT'
3636
licences = ['Apache-2.0']
3737
website = 'https://github.com/ypresto/android-transcoder'
3838
autoPublish = false
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright (C) 2014 Yuya Tanaka
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package net.ypresto.androidtranscoder.format;
17+
18+
import android.media.MediaCodecInfo;
19+
import android.media.MediaFormat;
20+
import android.util.Log;
21+
22+
class Android16By9FormatStrategy implements MediaFormatStrategy {
23+
public static final int SCALE_720P = 5;
24+
private static final String TAG = "Android16By9FormatStrategy";
25+
private final int mScale;
26+
private final int mBitrate;
27+
28+
public Android16By9FormatStrategy(int scale, int bitrate) {
29+
mScale = scale;
30+
mBitrate = bitrate;
31+
}
32+
33+
@Override
34+
public MediaFormat createVideoOutputFormat(MediaFormat inputFormat) {
35+
int width = inputFormat.getInteger(MediaFormat.KEY_WIDTH);
36+
int height = inputFormat.getInteger(MediaFormat.KEY_HEIGHT);
37+
int targetLonger = mScale * 16 * 16;
38+
int targetShorter = mScale * 16 * 9;
39+
int longer, shorter, outWidth, outHeight;
40+
if (width >= height) {
41+
longer = width;
42+
shorter = height;
43+
outWidth = targetLonger;
44+
outHeight = targetShorter;
45+
} else {
46+
shorter = width;
47+
longer = height;
48+
outWidth = targetShorter;
49+
outHeight = targetLonger;
50+
}
51+
if (longer * 9 != shorter * 16) {
52+
throw new OutputFormatUnavailableException("This video is not 16:9, and is not able to transcode. (" + width + "x" + height + ")");
53+
}
54+
if (shorter <= targetShorter) {
55+
Log.d(TAG, "This video's height is less or equal to " + targetShorter + ", pass-through. (" + width + "x" + height + ")");
56+
return null;
57+
}
58+
MediaFormat format = MediaFormat.createVideoFormat("video/avc", outWidth, outHeight);
59+
// From Nexus 4 Camera in 720p
60+
format.setInteger(MediaFormat.KEY_BIT_RATE, mBitrate);
61+
format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
62+
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 3);
63+
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
64+
return format;
65+
}
66+
67+
@Override
68+
public MediaFormat createAudioOutputFormat(MediaFormat inputFormat) {
69+
return null;
70+
}
71+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright (C) 2014 Yuya Tanaka
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package net.ypresto.androidtranscoder.format;
17+
18+
import android.media.MediaCodecInfo;
19+
import android.media.MediaFormat;
20+
import android.util.Log;
21+
22+
class Android720pFormatStrategy implements MediaFormatStrategy {
23+
private static final String TAG = "Android720pFormatStrategy";
24+
private static final int LONGER_LENGTH = 1280;
25+
private static final int SHORTER_LENGTH = 720;
26+
private static final int DEFAULT_BITRATE = 8000 * 1000; // From Nexus 4 Camera in 720p
27+
private final int mBitRate;
28+
29+
public Android720pFormatStrategy() {
30+
mBitRate = DEFAULT_BITRATE;
31+
}
32+
33+
public Android720pFormatStrategy(int bitRate) {
34+
mBitRate = bitRate;
35+
}
36+
37+
@Override
38+
public MediaFormat createVideoOutputFormat(MediaFormat inputFormat) {
39+
int width = inputFormat.getInteger(MediaFormat.KEY_WIDTH);
40+
int height = inputFormat.getInteger(MediaFormat.KEY_HEIGHT);
41+
int longer, shorter, outWidth, outHeight;
42+
if (width >= height) {
43+
longer = width;
44+
shorter = height;
45+
outWidth = LONGER_LENGTH;
46+
outHeight = SHORTER_LENGTH;
47+
} else {
48+
shorter = width;
49+
longer = height;
50+
outWidth = SHORTER_LENGTH;
51+
outHeight = LONGER_LENGTH;
52+
}
53+
if (longer * 9 != shorter * 16) {
54+
throw new OutputFormatUnavailableException("This video is not 16:9, and is not able to transcode. (" + width + "x" + height + ")");
55+
}
56+
if (shorter <= SHORTER_LENGTH) {
57+
Log.d(TAG, "This video is less or equal to 720p, pass-through. (" + width + "x" + height + ")");
58+
return null;
59+
}
60+
MediaFormat format = MediaFormat.createVideoFormat("video/avc", outWidth, outHeight);
61+
// From Nexus 4 Camera in 720p
62+
format.setInteger(MediaFormat.KEY_BIT_RATE, mBitRate);
63+
format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
64+
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 3);
65+
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
66+
return format;
67+
}
68+
69+
@Override
70+
public MediaFormat createAudioOutputFormat(MediaFormat inputFormat) {
71+
return null;
72+
}
73+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (C) 2014 Yuya Tanaka
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package net.ypresto.androidtranscoder.format;
17+
18+
import android.media.MediaFormat;
19+
import android.util.Log;
20+
21+
/**
22+
* Created by yuya.tanaka on 2014/11/20.
23+
*/
24+
class ExportPreset960x540Strategy implements MediaFormatStrategy {
25+
private static final String TAG = "ExportPreset960x540Strategy";
26+
27+
@Override
28+
public MediaFormat createVideoOutputFormat(MediaFormat inputFormat) {
29+
// TODO: detect non-baseline profile and throw exception
30+
int width = inputFormat.getInteger(MediaFormat.KEY_WIDTH);
31+
int height = inputFormat.getInteger(MediaFormat.KEY_HEIGHT);
32+
MediaFormat outputFormat = MediaFormatPresets.getExportPreset960x540(width, height);
33+
int outWidth = outputFormat.getInteger(MediaFormat.KEY_WIDTH);
34+
int outHeight = outputFormat.getInteger(MediaFormat.KEY_HEIGHT);
35+
Log.d(TAG, String.format("inputFormat: %dx%d => outputFormat: %dx%d", width, height, outWidth, outHeight));
36+
return outputFormat;
37+
}
38+
39+
@Override
40+
public MediaFormat createAudioOutputFormat(MediaFormat inputFormat) {
41+
// TODO
42+
return null;
43+
}
44+
}

lib/src/main/java/net/ypresto/androidtranscoder/format/MediaFormatPresets.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ public static MediaFormat getExportPreset960x540() {
3939
}
4040

4141
/**
42-
* Preset similar to iOS SDK's AVAssetExportPreset960x540
42+
* Preset similar to iOS SDK's AVAssetExportPreset960x540.
43+
* Note that encoding resolutions of this preset are not supported in all devices e.g. Nexus 4.
44+
* On unsupported device encoded video stream will be broken without any exception.
4345
* @param originalWidth Input video width.
4446
* @param originalHeight Input video height.
4547
* @return MediaFormat instance, or null if pass through.
@@ -53,7 +55,7 @@ public static MediaFormat getExportPreset960x540(int originalWidth, int original
5355
int residue = LONGER_LENGTH_960x540 * shorterLength % longerLength;
5456
if (residue != 0) {
5557
double ambiguousShorter = (double) LONGER_LENGTH_960x540 * shorterLength / longerLength;
56-
throw new IllegalArgumentException(String.format(
58+
throw new OutputFormatUnavailableException(String.format(
5759
"Could not fit to integer, original: (%d, %d), scaled: (%d, %f)",
5860
longerLength, shorterLength, LONGER_LENGTH_960x540, ambiguousShorter));
5961
}

lib/src/main/java/net/ypresto/androidtranscoder/format/MediaFormatStrategy.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,19 @@
2020
public interface MediaFormatStrategy {
2121

2222
/**
23+
* Returns preferred video format for encoding.
24+
*
2325
* @param inputFormat MediaFormat from MediaExtractor, contains csd-0/csd-1.
2426
* @return null for passthrough.
27+
* @throws OutputFormatUnavailableException if input could not be transcoded because of restrictions.
2528
*/
2629
public MediaFormat createVideoOutputFormat(MediaFormat inputFormat);
2730

2831
/**
2932
* Caution: this method should return null currently.
33+
*
3034
* @return null for passthrough.
35+
* @throws OutputFormatUnavailableException if input could not be transcoded because of restrictions.
3136
*/
3237
public MediaFormat createAudioOutputFormat(MediaFormat inputFormat);
3338

lib/src/main/java/net/ypresto/androidtranscoder/format/MediaFormatStrategyPresets.java

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,34 +15,40 @@
1515
*/
1616
package net.ypresto.androidtranscoder.format;
1717

18-
import android.media.MediaFormat;
19-
import android.util.Log;
20-
2118
public class MediaFormatStrategyPresets {
19+
/**
20+
* @deprecated Use {@link #createExportPreset960x540Strategy()}.
21+
*/
22+
@Deprecated
2223
public static final MediaFormatStrategy EXPORT_PRESET_960x540 = new ExportPreset960x540Strategy();
2324

24-
private MediaFormatStrategyPresets() {
25+
/**
26+
* Preset based on Nexus 4 camera recording with 720p quality.
27+
* This preset is ensured to work on any Android >=4.3 devices by Android CTS (if codec is available).
28+
*/
29+
public static MediaFormatStrategy createAndroid720pStrategy() {
30+
return new Android720pFormatStrategy();
2531
}
2632

27-
private static class ExportPreset960x540Strategy implements MediaFormatStrategy {
28-
private static final String TAG = "ExportPreset960x540Strategy";
33+
/**
34+
* Preset based on Nexus 4 camera recording with 720p quality.
35+
* This preset is ensured to work on any Android >=4.3 devices by Android CTS (if codec is available).
36+
*
37+
* @param bitRate Preferred bit rate for encoding.
38+
*/
39+
public static MediaFormatStrategy createAndroid720pStrategy(int bitRate) {
40+
return new Android720pFormatStrategy(bitRate);
41+
}
2942

30-
@Override
31-
public MediaFormat createVideoOutputFormat(MediaFormat inputFormat) {
32-
// TODO: detect non-baseline profile and throw exception
33-
int width = inputFormat.getInteger(MediaFormat.KEY_WIDTH);
34-
int height = inputFormat.getInteger(MediaFormat.KEY_HEIGHT);
35-
MediaFormat outputFormat = MediaFormatPresets.getExportPreset960x540(width, height);
36-
int outWidth = outputFormat.getInteger(MediaFormat.KEY_WIDTH);
37-
int outHeight = outputFormat.getInteger(MediaFormat.KEY_HEIGHT);
38-
Log.d(TAG, String.format("inputFormat: %dx%d => outputFormat: %dx%d", width, height, outWidth, outHeight));
39-
return outputFormat;
40-
}
43+
/**
44+
* Preset similar to iOS SDK's AVAssetExportPreset960x540.
45+
* Note that encoding resolutions of this preset are not supported in all devices e.g. Nexus 4.
46+
* On unsupported device encoded video stream will be broken without any exception.
47+
*/
48+
public static MediaFormatStrategy createExportPreset960x540Strategy() {
49+
return new ExportPreset960x540Strategy();
50+
}
4151

42-
@Override
43-
public MediaFormat createAudioOutputFormat(MediaFormat inputFormat) {
44-
// TODO
45-
return null;
46-
}
52+
private MediaFormatStrategyPresets() {
4753
}
4854
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright (C) 2014 Yuya Tanaka
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package net.ypresto.androidtranscoder.format;
17+
18+
public class OutputFormatUnavailableException extends RuntimeException {
19+
public OutputFormatUnavailableException(String detailMessage) {
20+
super(detailMessage);
21+
}
22+
}

0 commit comments

Comments
 (0)