diff options
Diffstat (limited to '')
19 files changed, 135 insertions, 2326 deletions
diff --git a/build.gradle b/build.gradle index 863c70814..ce3dea532 100644 --- a/build.gradle +++ b/build.gradle @@ -62,6 +62,7 @@ dependencies { compile 'pub.devrel:easypermissions:0.1.9' compile 'com.wefika:flowlayout:0.4.1' compile 'com.googlecode.ez-vcard:ez-vcard:0.10.0' + compile 'net.ypresto.androidtranscoder:android-transcoder:0.2.0' } ext { @@ -79,9 +80,8 @@ android { minSdkVersion 14 targetSdkVersion 25 - versionCode 182 - //versionName "1.15.4" - versionName "1.15.5 beta (2017-01-09)" + versionCode 183 + versionName "1.16.0 beta (2017-01-25)" archivesBaseName += "-$versionName" applicationId "de.pixart.messenger" diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index c1895848a..c8b696605 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -23,6 +23,8 @@ android:name="android.permission.READ_PHONE_STATE" tools:node="remove" /> + <uses-sdk tools:overrideLibrary="net.ypresto.androidtranscoder" /> + <application android:name="android.support.multidex.MultiDexApplication" android:allowBackup="true" diff --git a/src/main/java/de/pixart/messenger/Config.java b/src/main/java/de/pixart/messenger/Config.java index 9141f3944..058e3a85e 100644 --- a/src/main/java/de/pixart/messenger/Config.java +++ b/src/main/java/de/pixart/messenger/Config.java @@ -77,10 +77,6 @@ public final class Config { public static final int IMAGE_QUALITY = 75; public static final int IMAGE_MAX_SIZE = 1 * 1024 * 1024; // 1 MiB - public static final int VIDEO_SIZE = 640; - public static final int VIDEO_BITRATE = 2 * 1000 * 1000; // 2 Mbps - public static final int VIDEO_MAX_SIZE = 10 * 1024 * 1024; // 10 MiB - public static final int DEFAULT_ZOOM = 15; //for locations public static final int MESSAGE_MERGE_WINDOW = 20; diff --git a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java index 4578ef99a..35fb22a3f 100644 --- a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java +++ b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java @@ -16,12 +16,12 @@ import android.media.AudioManager; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; -import android.os.AsyncTask; import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.IBinder; +import android.os.ParcelFileDescriptor; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.SystemClock; @@ -29,7 +29,6 @@ import android.os.Vibrator; import android.preference.PreferenceManager; import android.provider.ContactsContract; import android.security.KeyChain; -import android.support.v4.app.NotificationCompat; import android.support.v4.app.RemoteInput; import android.util.DisplayMetrics; import android.util.Log; @@ -41,16 +40,15 @@ import net.java.otr4j.session.Session; import net.java.otr4j.session.SessionID; import net.java.otr4j.session.SessionImpl; import net.java.otr4j.session.SessionStatus; +import net.ypresto.androidtranscoder.MediaTranscoder; +import net.ypresto.androidtranscoder.format.MediaFormatStrategyPresets; import org.openintents.openpgp.IOpenPgpService2; import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpServiceConnection; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.FileDescriptor; +import java.io.FileNotFoundException; import java.math.BigInteger; import java.security.SecureRandom; import java.security.cert.CertificateException; @@ -61,7 +59,6 @@ import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; @@ -108,11 +105,10 @@ import de.pixart.messenger.persistance.DatabaseBackend; import de.pixart.messenger.persistance.FileBackend; import de.pixart.messenger.ui.SettingsActivity; import de.pixart.messenger.ui.UiCallback; +import de.pixart.messenger.ui.UiInformableCallback; import de.pixart.messenger.utils.ConversationsFileObserver; import de.pixart.messenger.utils.CryptoHelper; import de.pixart.messenger.utils.ExceptionHelper; -import de.pixart.messenger.utils.FileUtils; -import de.pixart.messenger.utils.FileWriterException; import de.pixart.messenger.utils.MimeUtils; import de.pixart.messenger.utils.OnPhoneContactsLoadedListener; import de.pixart.messenger.utils.PRNGFixes; @@ -121,7 +117,6 @@ import de.pixart.messenger.utils.ReplacingSerialSingleThreadExecutor; import de.pixart.messenger.utils.SerialSingleThreadExecutor; import de.pixart.messenger.utils.Xmlns; import de.pixart.messenger.utils.XmppUri; -import de.pixart.messenger.utils.video.MediaController; import de.pixart.messenger.xml.Element; import de.pixart.messenger.xmpp.OnBindListener; import de.pixart.messenger.xmpp.OnContactStatusChanged; @@ -147,8 +142,6 @@ import de.pixart.messenger.xmpp.stanzas.MessagePacket; import de.pixart.messenger.xmpp.stanzas.PresencePacket; import me.leolin.shortcutbadger.ShortcutBadger; -import static de.pixart.messenger.persistance.FileBackend.close; - public class XmppConnectionService extends Service { public static final String ACTION_REPLY_TO_CONVERSATION = "reply_to_conversations"; @@ -160,10 +153,8 @@ public class XmppConnectionService extends Service { public static final String ACTION_GCM_TOKEN_REFRESH = "gcm_token_refresh"; public static final String ACTION_GCM_MESSAGE_RECEIVED = "gcm_message_received"; private static final String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts"; - public static VideoCompressor CompressVideo; private final SerialSingleThreadExecutor mFileAddingExecutor = new SerialSingleThreadExecutor(); private final SerialSingleThreadExecutor mDatabaseExecutor = new SerialSingleThreadExecutor(); - private final SerialSingleThreadExecutor mCopyVideoExecutor = new SerialSingleThreadExecutor(); private final IBinder mBinder = new XmppConnectionBinder(); private final List<Conversation> conversations = new CopyOnWriteArrayList<>(); private final IqGenerator mIqGenerator = new IqGenerator(this); @@ -172,7 +163,6 @@ public class XmppConnectionService extends Service { public DatabaseBackend databaseBackend; private ReplacingSerialSingleThreadExecutor mContactMergerExecutor = new ReplacingSerialSingleThreadExecutor(true); private WakeLock wakeLock; - private WakeLock VideoCompressionWakeLock; private long mLastActivity = 0; private NotificationManager mNotifyManager; private ContentObserver contactObserver = new ContentObserver(null) { @@ -474,11 +464,9 @@ public class XmppConnectionService extends Service { } message.setCounterpart(conversation.getNextCounterpart()); message.setType(Message.TYPE_FILE); - final String path = getFileBackend().getOriginalPath(uri); - Log.d(Config.LOGTAG, "File path = " + path); mFileAddingExecutor.execute(new Runnable() { - @Override - public void run() { + private void processAsFile() { + final String path = getFileBackend().getOriginalPath(uri); if (path != null) { message.setRelativeFilePath(path); getFileBackend().updateFileParams(message); @@ -506,6 +494,72 @@ public class XmppConnectionService extends Service { } } } + + private void processAsVideo() throws FileNotFoundException { + Log.d(Config.LOGTAG, "processing file as video"); + message.setRelativeFilePath(message.getUuid() + ".mp4"); + final DownloadableFile file = getFileBackend().getFile(message); + file.getParentFile().mkdirs(); + ParcelFileDescriptor parcelFileDescriptor = getContentResolver().openFileDescriptor(uri, "r"); + FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); + final ArrayList<Integer> progressTracker = new ArrayList<>(); + final UiInformableCallback<Message> informableCallback; + if (callback instanceof UiInformableCallback) { + informableCallback = (UiInformableCallback<Message>) callback; + } else { + informableCallback = null; + } + MediaTranscoder.Listener listener = new MediaTranscoder.Listener() { + @Override + public void onTranscodeProgress(double progress) { + int p = ((int) Math.round(progress * 100) / 5) * 5; + if (!progressTracker.contains(p) && p != 100 && p != 0) { + progressTracker.add(p); + if (informableCallback != null) { + + informableCallback.inform(getString(R.string.transcoding_video_progress, p)); + } + } + } + + @Override + public void onTranscodeCompleted() { + if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { + getPgpEngine().encrypt(message, callback); + } else { + callback.success(message); + } + } + + @Override + public void onTranscodeCanceled() { + processAsFile(); + } + + @Override + public void onTranscodeFailed(Exception e) { + Log.d(Config.LOGTAG, "video transcoding failed " + e.getMessage()); + processAsFile(); + } + }; + MediaTranscoder.getInstance().transcodeVideo(fileDescriptor, file.getAbsolutePath(), + MediaFormatStrategyPresets.createAndroid720pStrategy(), listener); + } + + @Override + public void run() { + final String mimeType = MimeUtils.guessMimeTypeFromUri(XmppConnectionService.this, uri); + if (mimeType != null && mimeType.startsWith("video/") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + try { + processAsVideo(); + } catch (Throwable e) { + processAsFile(); + } + } else { + processAsFile(); + } + + } }); } @@ -555,143 +609,6 @@ public class XmppConnectionService extends Service { }); } - public void attachVideoToConversation(final Conversation conversation, final Uri uri, final UiCallback<Message> callback) { - final Integer NOTIFICATION_ID = (int) (new Date().getTime() / 1000); - final int[] success = {-1}; - File outputDir = this.getCacheDir(); - File file = null; - try { - file = File.createTempFile("temp-", "." + FileUtils.getExtension(this, uri), outputDir); - } catch (IOException e) { - Log.d(Config.LOGTAG, "error creating temp file"); - } - long filesize = 0; - String path = null; - Boolean error = false; - - final File finalFile = file; - new Thread(new Runnable() { - - @Override - public void run() { - try { - finalFile.getParentFile().mkdirs(); - Log.d(Config.LOGTAG, "copy video (" + uri.toString() + ") to temporarly storage " + finalFile.getAbsolutePath()); - OutputStream os = null; - InputStream is = null; - try { - finalFile.createNewFile(); - os = new FileOutputStream(finalFile); - is = XmppConnectionService.this.getContentResolver().openInputStream(uri); - byte[] buffer = new byte[1024]; - int length; - while ((length = is.read(buffer)) > 0) { - try { - os.write(buffer, 0, length); - } catch (IOException e) { - throw new FileWriterException(); - } - } - try { - os.flush(); - } catch (IOException e) { - throw new FileWriterException(); - } - } catch (Exception e) { - Log.d(Config.LOGTAG, "cannot copy video " + e); - success[0] = 2; - } finally { - success[0] = 1; - close(os); - close(is); - } - } catch (Exception e) { - Log.d(Config.LOGTAG, "cannot copy video " + e); - } - } - }).start(); - - while (success[0] == -1) { - //ignored - //Log.d(Config.LOGTAG, "Wait while copying video"); - } - if (success[0] == 2) { - mNotifyManager.cancel(NOTIFICATION_ID); - callback.error(R.string.error_file_corrupt, null); - return; - } - if (success[0] == 1) { - try { - filesize = file.length(); - path = file.toString(); - } catch (Exception e) { - error = true; - Log.d(Config.LOGTAG, "Error retrieving file path " + e); - } - final String compressVideos = getCompressVideoPreference(); - boolean sendVideoAsIs = false; - mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext()); - mBuilder.setContentTitle(getString(R.string.app_name)) - .setContentText(getString(R.string.compressing_video)) - .setSmallIcon(R.drawable.ic_play_box_outline_white_24dp) - .setOngoing(true) - .setProgress(0, 0, true); - mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build()); - if (FileBackend.weOwnFile(this, uri)) { - Log.d(Config.LOGTAG, "trying to attach video that belonged to us"); - mNotifyManager.cancel(NOTIFICATION_ID); - callback.error(R.string.security_error_invalid_file_access, null); - return; - } - if (file == null) { - error = true; - } - if (filesize == 0) { - error = true; - } - if (error) { - Log.d(Config.LOGTAG, "Error with file, size = 0"); - mNotifyManager.cancel(NOTIFICATION_ID); - callback.error(R.string.error_file_corrupt, null); - return; - } - Log.d(Config.LOGTAG, "Video file (size) :" + file.toString() + "(" + filesize / 1024 / 1024 + "MB)"); - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US); - File compressed_file = new File(FileBackend.getConversationsVideoDirectory() + "/" - + dateFormat.format(new Date()) - + "_komp.mp4"); - final String compressed_path = compressed_file.toString(); - final Uri compressed_uri = Uri.fromFile(compressed_file); - if ("never".equals(compressVideos)) { - sendVideoAsIs = true; - } else if ("always".equals(compressVideos) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - sendVideoAsIs = false; - } else if ("auto".equals(compressVideos) && filesize > Config.VIDEO_MAX_SIZE && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - sendVideoAsIs = false; - } else { - sendVideoAsIs = true; - } - if (!sendVideoAsIs) { - CompressVideo = new VideoCompressor(path, compressed_path, new Interface() { - @Override - public void videocompressed(boolean result) { - if (result) { - Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": sending compressed video."); - mNotifyManager.cancel(NOTIFICATION_ID); - attachFileToConversation(conversation, compressed_uri, callback); - } - } - }); - CompressVideo.execute(); - } else { - Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": not compressing video. sending as file"); - mNotifyManager.cancel(NOTIFICATION_ID); - attachFileToConversation(conversation, uri, callback); - } - } - } - public Conversation find(Bookmark bookmark) { return find(bookmark.getAccount(), bookmark.getJid()); } @@ -941,10 +858,6 @@ public class XmppConnectionService extends Service { return getPreferences().getString("picture_compression", "auto"); } - private String getCompressVideoPreference() { - return getPreferences().getString("video_compression", "auto"); - } - private Presence.Status getTargetPresence() { if (xaOnSilentMode() && isPhoneSilenced()) { return Presence.Status.XA; @@ -1110,7 +1023,6 @@ public class XmppConnectionService extends Service { this.pm = (PowerManager) getSystemService(Context.POWER_SERVICE); this.wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "XmppConnectionService"); - this.VideoCompressionWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "VideoCompression"); toggleForegroundService(); updateUnreadCountBadge(); toggleScreenEventReceiver(); @@ -3943,10 +3855,6 @@ public class XmppConnectionService extends Service { } } - public interface Interface { - void videocompressed(boolean result); - } - public interface OnMamPreferencesFetched { void onPreferencesFetched(Element prefs); @@ -4026,46 +3934,6 @@ public class XmppConnectionService extends Service { void onShowErrorToast(int resId); } - class VideoCompressor extends AsyncTask<String, Void, Boolean> { - private String originalpath; - private String compressedpath; - private Interface mListener; - - VideoCompressor(String path, String compressed_path, Interface mListener) { - originalpath = path; - compressedpath = compressed_path; - this.mListener = mListener; - } - - @Override - protected void onPreExecute() { - super.onPreExecute(); - Log.d(Config.LOGTAG, "Start video compression"); - VideoCompressionWakeLock.acquire(); - } - - @Override - protected Boolean doInBackground(String... params) { - return MediaController.getInstance().convertVideo(originalpath, compressedpath); - } - - @Override - protected void onPostExecute(Boolean compressed) { - super.onPostExecute(compressed); - File video = new File(compressedpath); - if (mListener != null) { - if (video.exists() && video.length() > 0) { - mListener.videocompressed(compressed); - Log.d(Config.LOGTAG, "Compression successfully!"); - } else { - mListener.videocompressed(false); - Log.d(Config.LOGTAG, "Compression failed!"); - } - } - VideoCompressionWakeLock.release(); - } - } - public class XmppConnectionBinder extends Binder { public XmppConnectionService getService() { return XmppConnectionService.this; diff --git a/src/main/java/de/pixart/messenger/ui/ConversationActivity.java b/src/main/java/de/pixart/messenger/ui/ConversationActivity.java index a68765632..529c926aa 100644 --- a/src/main/java/de/pixart/messenger/ui/ConversationActivity.java +++ b/src/main/java/de/pixart/messenger/ui/ConversationActivity.java @@ -117,7 +117,6 @@ public class ConversationActivity extends XmppActivity final private List<Uri> mPendingImageUris = new ArrayList<>(); final private List<Uri> mPendingPhotoUris = new ArrayList<>(); final private List<Uri> mPendingFileUris = new ArrayList<>(); - final private List<Uri> mPendingVideoUris = new ArrayList<>(); private String mOpenConversation = null; private boolean mPanelOpen = true; private AtomicBoolean mShouldPanelBeOpen = new AtomicBoolean(false); @@ -674,9 +673,10 @@ public class ConversationActivity extends XmppActivity chooser = true; break; case ATTACHMENT_CHOICE_CHOOSE_VIDEO: - intent.setAction(Intent.ACTION_GET_CONTENT); - intent.setType("video/*"); chooser = true; + intent.setType("video/*"); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setAction(Intent.ACTION_GET_CONTENT); break; case ATTACHMENT_CHOICE_TAKE_PHOTO: Uri uri = xmppConnectionService.getFileBackend().getTakePhotoUri(); @@ -1370,7 +1370,6 @@ public class ConversationActivity extends XmppActivity private void clearPending() { mPendingImageUris.clear(); mPendingFileUris.clear(); - mPendingVideoUris.clear(); mPendingPhotoUris.clear(); mPendingGeoUri = null; mPostponedActivityResult = null; @@ -1509,11 +1508,6 @@ public class ConversationActivity extends XmppActivity attachPhotoToConversation(getSelectedConversation(), i.next()); } - for (Iterator<Uri> i = mPendingVideoUris.iterator(); i.hasNext(); i.remove()) { - Log.d(Config.LOGTAG, "ConversationActivity.onBackendConnected() - attaching video to conversations. stopping=" + Boolean.toString(stopping)); - attachVideoToConversation(getSelectedConversation(), i.next()); - } - for (Iterator<Uri> i = mPendingFileUris.iterator(); i.hasNext(); i.remove()) { Log.d(Config.LOGTAG, "ConversationActivity.onBackendConnected() - attaching file to conversations. stopping=" + Boolean.toString(stopping)); attachFileToConversation(getSelectedConversation(), i.next()); @@ -1659,7 +1653,7 @@ public class ConversationActivity extends XmppActivity } } } - } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_FILE || requestCode == ATTACHMENT_CHOICE_RECORD_VOICE) { + } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_FILE || requestCode == ATTACHMENT_CHOICE_RECORD_VOICE || requestCode == ATTACHMENT_CHOICE_CHOOSE_VIDEO) { final List<Uri> uris = extractUriFromIntent(data); final Conversation c = getSelectedConversation(); final OnPresenceSelected callback = new OnPresenceSelected() { @@ -1682,29 +1676,6 @@ public class ConversationActivity extends XmppActivity } else { selectPresence(c, callback); } - } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_VIDEO) { - final List<Uri> uris = extractUriFromIntent(data); - final Conversation c = getSelectedConversation(); - final OnPresenceSelected callback = new OnPresenceSelected() { - @Override - public void onPresenceSelected() { - mPendingVideoUris.clear(); - mPendingVideoUris.addAll(uris); - if (xmppConnectionServiceBound) { - for (Iterator<Uri> i = mPendingVideoUris.iterator(); i.hasNext(); i.remove()) { - Log.d(Config.LOGTAG, "ConversationActivity.onActivityResult() - attaching video to conversations. CHOOSE_VIDEO"); - attachVideoToConversation(c, i.next()); - } - } - } - }; - if (c == null || c.getMode() == Conversation.MODE_MULTI - || FileBackend.allFilesUnderSize(this, uris, getMaxHttpUploadSize(c)) - || c.getNextEncryption() == Message.ENCRYPTION_OTR) { - callback.onPresenceSelected(); - } else { - selectPresence(c, callback); - } } else if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO) { if (mPendingPhotoUris.size() == 1) { Uri uri = FileBackend.getIndexableTakePhotoUri(mPendingPhotoUris.get(0)); @@ -1831,41 +1802,26 @@ public class ConversationActivity extends XmppActivity } final Toast prepareFileToast = Toast.makeText(getApplicationContext(), getText(R.string.preparing_file), Toast.LENGTH_LONG); prepareFileToast.show(); - xmppConnectionService.attachFileToConversation(conversation, uri, new UiCallback<Message>() { + xmppConnectionService.attachFileToConversation(conversation, uri, new UiInformableCallback<Message>() { @Override - public void success(Message message) { - hidePrepareFileToast(prepareFileToast); - xmppConnectionService.sendMessage(message); - } - - @Override - public void error(final int errorCode, Message message) { + public void inform(final String text) { hidePrepareFileToast(prepareFileToast); runOnUiThread(new Runnable() { @Override public void run() { - replaceToast(getString(errorCode)); + replaceToast(text); } }); - - } - - @Override - public void userInputRequried(PendingIntent pi, Message message) { - hidePrepareFileToast(prepareFileToast); } - }); - } - private void attachVideoToConversation(Conversation conversation, final Uri uri) { - if (conversation == null) { - return; - } - final Toast prepareFileToast = Toast.makeText(getApplicationContext(), getText(R.string.preparing_video), Toast.LENGTH_LONG); - prepareFileToast.show(); - xmppConnectionService.attachVideoToConversation(conversation, uri, new UiCallback<Message>() { @Override public void success(Message message) { + runOnUiThread(new Runnable() { + @Override + public void run() { + hideToast(); + } + }); hidePrepareFileToast(prepareFileToast); xmppConnectionService.sendMessage(message); } diff --git a/src/main/java/de/pixart/messenger/ui/ShareWithActivity.java b/src/main/java/de/pixart/messenger/ui/ShareWithActivity.java index 870812cc4..c2cf95241 100644 --- a/src/main/java/de/pixart/messenger/ui/ShareWithActivity.java +++ b/src/main/java/de/pixart/messenger/ui/ShareWithActivity.java @@ -49,7 +49,6 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer private class Share { public List<Uri> uris = new ArrayList<>(); public boolean image; - public boolean video; public String account; public String contact; public String text; @@ -66,7 +65,17 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer private Toast mToast; private AtomicInteger attachmentCounter = new AtomicInteger(0); - private UiCallback<Message> attachFileCallback = new UiCallback<Message>() { + private UiInformableCallback<Message> attachFileCallback = new UiInformableCallback<Message>() { + + @Override + public void inform(final String text) { + runOnUiThread(new Runnable() { + @Override + public void run() { + replaceToast(text); + } + }); + } @Override public void userInputRequried(PendingIntent pi, Message object) { @@ -86,8 +95,6 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer resId = R.string.shared_images_with_x; } else if (share.image) { resId = R.string.shared_image_with_x; - } else if (share.video) { - resId = R.string.shared_video_with_x; } else { resId = R.string.shared_file_with_x; } @@ -219,7 +226,6 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer this.share.uris.clear(); this.share.uris.add(uri); this.share.image = type.startsWith("image/") || isImage(uri); - this.share.video = type.startsWith("video/") || isVideo(uri); } else { if (subject != null) { this.share.text = format("[%s]%n%s", subject, text); @@ -265,15 +271,6 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer } } - protected boolean isVideo(Uri uri) { - try { - String guess = URLConnection.guessContentTypeFromName(uri.toString()); - return (guess != null && guess.startsWith("video/")); - } catch (final StringIndexOutOfBoundsException ignored) { - return false; - } - } - @Override void onBackendConnected() { if (xmppConnectionServiceBound && share != null @@ -341,24 +338,16 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer .attachImageToConversation(conversation, i.next(), attachFileCallback); } - } else if (share.video) { - Log.d(Config.LOGTAG, "ShareWithActivity share() video " + share.uris.size() + " uri(s) " + share.uris.toString()); - replaceToast(getString(R.string.preparing_video)); - ShareWithActivity.this.xmppConnectionService - .attachVideoToConversation(conversation, share.uris.get(0), - attachFileCallback); } else { Log.d(Config.LOGTAG, "ShareWithActivity share() file " + share.uris.size() + " uri(s) " + share.uris.toString()); replaceToast(getString(R.string.preparing_file)); ShareWithActivity.this.xmppConnectionService - .attachFileToConversation(conversation, share.uris.get(0), - attachFileCallback); + .attachFileToConversation(conversation, share.uris.get(0), attachFileCallback); } } }; if (account.httpUploadAvailable() && ((share.image && !neverCompressPictures()) - || share.video || conversation.getMode() == Conversation.MODE_MULTI || FileBackend.allFilesUnderSize(this, share.uris, max)) && conversation.getNextEncryption() != Message.ENCRYPTION_OTR) { diff --git a/src/main/java/de/pixart/messenger/ui/ShowFullscreenMessageActivity.java b/src/main/java/de/pixart/messenger/ui/ShowFullscreenMessageActivity.java index 5f1fdc3c7..a8253fd1c 100644 --- a/src/main/java/de/pixart/messenger/ui/ShowFullscreenMessageActivity.java +++ b/src/main/java/de/pixart/messenger/ui/ShowFullscreenMessageActivity.java @@ -30,8 +30,6 @@ import java.io.InputStream; import de.pixart.messenger.Config; import de.pixart.messenger.R; -import de.pixart.messenger.persistance.FileBackend; -import de.pixart.messenger.services.XmppConnectionService; import de.pixart.messenger.utils.ExifHelper; import uk.co.senab.photoview.PhotoView; import uk.co.senab.photoview.PhotoViewAttacher; @@ -50,8 +48,6 @@ public class ShowFullscreenMessageActivity extends Activity { int height = 0; int width = 0; int rotation = 0; - FileBackend mFileBackend; - XmppConnectionService mXmppConnectionService; @Override public void onCreate(Bundle savedInstanceState) { @@ -194,18 +190,35 @@ public class ShowFullscreenMessageActivity extends Activity { retriever.setDataSource(uri.getPath()); height = Integer.valueOf(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)); width = Integer.valueOf(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)); - Log.d(Config.LOGTAG, "Video height: " + height + ", width: " + width); + rotation = Integer.valueOf(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION)); + Log.d(Config.LOGTAG, "Video height: " + height + ", width: " + width + ", rotation: " + rotation); if (width > height) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE); + if (rotation == 0 || rotation == 180) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE); + } else { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + } } else { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT); + } else { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } } } else if (width <= height) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT); + if (rotation == 90 || rotation == 270) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT); + } else { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } } else { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT); + } else { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } } } try { diff --git a/src/main/java/de/pixart/messenger/ui/UiInformableCallback.java b/src/main/java/de/pixart/messenger/ui/UiInformableCallback.java new file mode 100644 index 000000000..53ed044cc --- /dev/null +++ b/src/main/java/de/pixart/messenger/ui/UiInformableCallback.java @@ -0,0 +1,5 @@ +package de.pixart.messenger.ui; + +public interface UiInformableCallback<T> extends UiCallback<T> { + void inform(String text); +}
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/utils/video/InputSurface.java b/src/main/java/de/pixart/messenger/utils/video/InputSurface.java deleted file mode 100644 index 3376b4b87..000000000 --- a/src/main/java/de/pixart/messenger/utils/video/InputSurface.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.pixart.messenger.utils.video; - -import android.annotation.TargetApi; -import android.opengl.EGL14; -import android.opengl.EGLConfig; -import android.opengl.EGLContext; -import android.opengl.EGLDisplay; -import android.opengl.EGLExt; -import android.opengl.EGLSurface; -import android.os.Build; -import android.view.Surface; - -@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) -public class InputSurface { - private static final boolean VERBOSE = false; - private static final int EGL_RECORDABLE_ANDROID = 0x3142; - private static final int EGL_OPENGL_ES2_BIT = 4; - private EGLDisplay mEGLDisplay; - private EGLContext mEGLContext; - private EGLSurface mEGLSurface; - private Surface mSurface; - - public InputSurface(Surface surface) { - if (surface == null) { - throw new NullPointerException(); - } - mSurface = surface; - eglSetup(); - } - - private void eglSetup() { - mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); - if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) { - throw new RuntimeException("unable to get EGL14 display"); - } - int[] version = new int[2]; - if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) { - mEGLDisplay = null; - throw new RuntimeException("unable to initialize EGL14"); - } - - int[] attribList = { - EGL14.EGL_RED_SIZE, 8, - EGL14.EGL_GREEN_SIZE, 8, - EGL14.EGL_BLUE_SIZE, 8, - EGL14.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_RECORDABLE_ANDROID, 1, - EGL14.EGL_NONE - }; - EGLConfig[] configs = new EGLConfig[1]; - int[] numConfigs = new int[1]; - if (!EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length, - numConfigs, 0)) { - throw new RuntimeException("unable to find RGB888+recordable ES2 EGL config"); - } - - int[] attrib_list = { - EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, - EGL14.EGL_NONE - }; - - mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT, attrib_list, 0); - checkEglError("eglCreateContext"); - if (mEGLContext == null) { - throw new RuntimeException("null context"); - } - - int[] surfaceAttribs = { - EGL14.EGL_NONE - }; - mEGLSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, configs[0], mSurface, - surfaceAttribs, 0); - checkEglError("eglCreateWindowSurface"); - if (mEGLSurface == null) { - throw new RuntimeException("surface was null"); - } - } - - public void release() { - if (EGL14.eglGetCurrentContext().equals(mEGLContext)) { - EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT); - } - EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface); - EGL14.eglDestroyContext(mEGLDisplay, mEGLContext); - mSurface.release(); - mEGLDisplay = null; - mEGLContext = null; - mEGLSurface = null; - mSurface = null; - } - - public void makeCurrent() { - if (!EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) { - throw new RuntimeException("eglMakeCurrent failed"); - } - } - - public boolean swapBuffers() { - return EGL14.eglSwapBuffers(mEGLDisplay, mEGLSurface); - } - - public Surface getSurface() { - return mSurface; - } - - public void setPresentationTime(long nsecs) { - EGLExt.eglPresentationTimeANDROID(mEGLDisplay, mEGLSurface, nsecs); - } - - private void checkEglError(String msg) { - boolean failed = false; - int error; - while ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) { - failed = true; - } - if (failed) { - throw new RuntimeException("EGL error encountered (see log)"); - } - } -} diff --git a/src/main/java/de/pixart/messenger/utils/video/MP4Builder.java b/src/main/java/de/pixart/messenger/utils/video/MP4Builder.java deleted file mode 100644 index ee8b83e27..000000000 --- a/src/main/java/de/pixart/messenger/utils/video/MP4Builder.java +++ /dev/null @@ -1,445 +0,0 @@ -/* - * This is the source code of Telegram for Android v. 1.7.x. - * It is licensed under GNU GPL v. 2 or later. - * You should have received a copy of the license in this archive (see LICENSE). - * - * Copyright Nikolai Kudashov, 2013-2014. - */ - -package de.pixart.messenger.utils.video; - -import android.annotation.TargetApi; -import android.media.MediaCodec; -import android.media.MediaFormat; - -import com.coremedia.iso.BoxParser; -import com.coremedia.iso.IsoFile; -import com.coremedia.iso.IsoTypeWriter; -import com.coremedia.iso.boxes.Box; -import com.coremedia.iso.boxes.Container; -import com.coremedia.iso.boxes.DataEntryUrlBox; -import com.coremedia.iso.boxes.DataInformationBox; -import com.coremedia.iso.boxes.DataReferenceBox; -import com.coremedia.iso.boxes.FileTypeBox; -import com.coremedia.iso.boxes.HandlerBox; -import com.coremedia.iso.boxes.MediaBox; -import com.coremedia.iso.boxes.MediaHeaderBox; -import com.coremedia.iso.boxes.MediaInformationBox; -import com.coremedia.iso.boxes.MovieBox; -import com.coremedia.iso.boxes.MovieHeaderBox; -import com.coremedia.iso.boxes.SampleSizeBox; -import com.coremedia.iso.boxes.SampleTableBox; -import com.coremedia.iso.boxes.SampleToChunkBox; -import com.coremedia.iso.boxes.StaticChunkOffsetBox; -import com.coremedia.iso.boxes.SyncSampleBox; -import com.coremedia.iso.boxes.TimeToSampleBox; -import com.coremedia.iso.boxes.TrackBox; -import com.coremedia.iso.boxes.TrackHeaderBox; -import com.googlecode.mp4parser.DataSource; -import com.googlecode.mp4parser.util.Matrix; - -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.channels.WritableByteChannel; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; - -@TargetApi(16) -public class MP4Builder { - - private InterleaveChunkMdat mdat = null; - private Mp4Movie currentMp4Movie = null; - private FileOutputStream fos = null; - private FileChannel fc = null; - private long dataOffset = 0; - private long writedSinceLastMdat = 0; - private boolean writeNewMdat = true; - private HashMap<Track, long[]> track2SampleSizes = new HashMap<>(); - private ByteBuffer sizeBuffer = null; - - public MP4Builder createMovie(Mp4Movie mp4Movie) throws Exception { - currentMp4Movie = mp4Movie; - - fos = new FileOutputStream(mp4Movie.getCacheFile()); - fc = fos.getChannel(); - - FileTypeBox fileTypeBox = createFileTypeBox(); - fileTypeBox.getBox(fc); - dataOffset += fileTypeBox.getSize(); - writedSinceLastMdat += dataOffset; - - mdat = new InterleaveChunkMdat(); - - sizeBuffer = ByteBuffer.allocateDirect(4); - - return this; - } - - private void flushCurrentMdat() throws Exception { - long oldPosition = fc.position(); - fc.position(mdat.getOffset()); - mdat.getBox(fc); - fc.position(oldPosition); - mdat.setDataOffset(0); - mdat.setContentSize(0); - fos.flush(); - } - - public boolean writeSampleData(int trackIndex, ByteBuffer byteBuf, MediaCodec.BufferInfo bufferInfo, boolean isAudio) throws Exception { - if (writeNewMdat) { - mdat.setContentSize(0); - mdat.getBox(fc); - mdat.setDataOffset(dataOffset); - dataOffset += 16; - writedSinceLastMdat += 16; - writeNewMdat = false; - } - - mdat.setContentSize(mdat.getContentSize() + bufferInfo.size); - writedSinceLastMdat += bufferInfo.size; - - boolean flush = false; - if (writedSinceLastMdat >= 32 * 1024) { - flushCurrentMdat(); - writeNewMdat = true; - flush = true; - writedSinceLastMdat -= 32 * 1024; - } - - currentMp4Movie.addSample(trackIndex, dataOffset, bufferInfo); - byteBuf.position(bufferInfo.offset + (isAudio ? 0 : 4)); - byteBuf.limit(bufferInfo.offset + bufferInfo.size); - - if (!isAudio) { - sizeBuffer.position(0); - sizeBuffer.putInt(bufferInfo.size - 4); - sizeBuffer.position(0); - fc.write(sizeBuffer); - } - - fc.write(byteBuf); - dataOffset += bufferInfo.size; - - if (flush) { - fos.flush(); - } - return flush; - } - - public int addTrack(MediaFormat mediaFormat, boolean isAudio) throws Exception { - return currentMp4Movie.addTrack(mediaFormat, isAudio); - } - - public void finishMovie(boolean error) throws Exception { - if (mdat.getContentSize() != 0) { - flushCurrentMdat(); - } - - for (Track track : currentMp4Movie.getTracks()) { - List<Sample> samples = track.getSamples(); - long[] sizes = new long[samples.size()]; - for (int i = 0; i < sizes.length; i++) { - sizes[i] = samples.get(i).getSize(); - } - track2SampleSizes.put(track, sizes); - } - - Box moov = createMovieBox(currentMp4Movie); - moov.getBox(fc); - fos.flush(); - - fc.close(); - fos.close(); - } - - protected FileTypeBox createFileTypeBox() { - LinkedList<String> minorBrands = new LinkedList<>(); - minorBrands.add("isom"); - minorBrands.add("3gp4"); - return new FileTypeBox("isom", 0, minorBrands); - } - - private class InterleaveChunkMdat implements Box { - private Container parent; - private long contentSize = 1024 * 1024 * 1024; - private long dataOffset = 0; - - public Container getParent() { - return parent; - } - - public long getOffset() { - return dataOffset; - } - - public void setDataOffset(long offset) { - dataOffset = offset; - } - - public void setParent(Container parent) { - this.parent = parent; - } - - public void setContentSize(long contentSize) { - this.contentSize = contentSize; - } - - public long getContentSize() { - return contentSize; - } - - public String getType() { - return "mdat"; - } - - public long getSize() { - return 16 + contentSize; - } - - private boolean isSmallBox(long contentSize) { - return (contentSize + 8) < 4294967296L; - } - - @Override - public void parse(DataSource dataSource, ByteBuffer header, long contentSize, BoxParser boxParser) throws IOException { - - } - - public void getBox(WritableByteChannel writableByteChannel) throws IOException { - ByteBuffer bb = ByteBuffer.allocate(16); - long size = getSize(); - if (isSmallBox(size)) { - IsoTypeWriter.writeUInt32(bb, size); - } else { - IsoTypeWriter.writeUInt32(bb, 1); - } - bb.put(IsoFile.fourCCtoBytes("mdat")); - if (isSmallBox(size)) { - bb.put(new byte[8]); - } else { - IsoTypeWriter.writeUInt64(bb, size); - } - bb.rewind(); - writableByteChannel.write(bb); - } - } - - public static long gcd(long a, long b) { - if (b == 0) { - return a; - } - return gcd(b, a % b); - } - - public long getTimescale(Mp4Movie mp4Movie) { - long timescale = 0; - if (!mp4Movie.getTracks().isEmpty()) { - timescale = mp4Movie.getTracks().iterator().next().getTimeScale(); - } - for (Track track : mp4Movie.getTracks()) { - timescale = gcd(track.getTimeScale(), timescale); - } - return timescale; - } - - protected MovieBox createMovieBox(Mp4Movie movie) { - MovieBox movieBox = new MovieBox(); - MovieHeaderBox mvhd = new MovieHeaderBox(); - - mvhd.setCreationTime(new Date()); - mvhd.setModificationTime(new Date()); - mvhd.setMatrix(Matrix.ROTATE_0); - long movieTimeScale = getTimescale(movie); - long duration = 0; - - for (Track track : movie.getTracks()) { - long tracksDuration = track.getDuration() * movieTimeScale / track.getTimeScale(); - if (tracksDuration > duration) { - duration = tracksDuration; - } - } - - mvhd.setDuration(duration); - mvhd.setTimescale(movieTimeScale); - mvhd.setNextTrackId(movie.getTracks().size() + 1); - - movieBox.addBox(mvhd); - for (Track track : movie.getTracks()) { - movieBox.addBox(createTrackBox(track, movie)); - } - return movieBox; - } - - protected TrackBox createTrackBox(Track track, Mp4Movie movie) { - TrackBox trackBox = new TrackBox(); - TrackHeaderBox tkhd = new TrackHeaderBox(); - - tkhd.setEnabled(true); - tkhd.setInMovie(true); - tkhd.setInPreview(true); - if (track.isAudio()) { - tkhd.setMatrix(Matrix.ROTATE_0); - } else { - tkhd.setMatrix(movie.getMatrix()); - } - tkhd.setAlternateGroup(0); - tkhd.setCreationTime(track.getCreationTime()); - tkhd.setDuration(track.getDuration() * getTimescale(movie) / track.getTimeScale()); - tkhd.setHeight(track.getHeight()); - tkhd.setWidth(track.getWidth()); - tkhd.setLayer(0); - tkhd.setModificationTime(new Date()); - tkhd.setTrackId(track.getTrackId() + 1); - tkhd.setVolume(track.getVolume()); - - trackBox.addBox(tkhd); - - MediaBox mdia = new MediaBox(); - trackBox.addBox(mdia); - MediaHeaderBox mdhd = new MediaHeaderBox(); - mdhd.setCreationTime(track.getCreationTime()); - mdhd.setDuration(track.getDuration()); - mdhd.setTimescale(track.getTimeScale()); - mdhd.setLanguage("eng"); - mdia.addBox(mdhd); - HandlerBox hdlr = new HandlerBox(); - hdlr.setName(track.isAudio() ? "SoundHandle" : "VideoHandle"); - hdlr.setHandlerType(track.getHandler()); - - mdia.addBox(hdlr); - - MediaInformationBox minf = new MediaInformationBox(); - minf.addBox(track.getMediaHeaderBox()); - - DataInformationBox dinf = new DataInformationBox(); - DataReferenceBox dref = new DataReferenceBox(); - dinf.addBox(dref); - DataEntryUrlBox url = new DataEntryUrlBox(); - url.setFlags(1); - dref.addBox(url); - minf.addBox(dinf); - - Box stbl = createStbl(track); - minf.addBox(stbl); - mdia.addBox(minf); - - return trackBox; - } - - protected Box createStbl(Track track) { - SampleTableBox stbl = new SampleTableBox(); - - createStsd(track, stbl); - createStts(track, stbl); - createStss(track, stbl); - createStsc(track, stbl); - createStsz(track, stbl); - createStco(track, stbl); - - return stbl; - } - - protected void createStsd(Track track, SampleTableBox stbl) { - stbl.addBox(track.getSampleDescriptionBox()); - } - - protected void createStts(Track track, SampleTableBox stbl) { - TimeToSampleBox.Entry lastEntry = null; - List<TimeToSampleBox.Entry> entries = new ArrayList<>(); - - for (long delta : track.getSampleDurations()) { - if (lastEntry != null && lastEntry.getDelta() == delta) { - lastEntry.setCount(lastEntry.getCount() + 1); - } else { - lastEntry = new TimeToSampleBox.Entry(1, delta); - entries.add(lastEntry); - } - } - TimeToSampleBox stts = new TimeToSampleBox(); - stts.setEntries(entries); - stbl.addBox(stts); - } - - protected void createStss(Track track, SampleTableBox stbl) { - long[] syncSamples = track.getSyncSamples(); - if (syncSamples != null && syncSamples.length > 0) { - SyncSampleBox stss = new SyncSampleBox(); - stss.setSampleNumber(syncSamples); - stbl.addBox(stss); - } - } - - protected void createStsc(Track track, SampleTableBox stbl) { - SampleToChunkBox stsc = new SampleToChunkBox(); - stsc.setEntries(new LinkedList<SampleToChunkBox.Entry>()); - - long lastOffset = -1; - int lastChunkNumber = 1; - int lastSampleCount = 0; - - int previousWritedChunkCount = -1; - - int samplesCount = track.getSamples().size(); - for (int a = 0; a < samplesCount; a++) { - Sample sample = track.getSamples().get(a); - long offset = sample.getOffset(); - long size = sample.getSize(); - - lastOffset = offset + size; - lastSampleCount++; - - boolean write = false; - if (a != samplesCount - 1) { - Sample nextSample = track.getSamples().get(a + 1); - if (lastOffset != nextSample.getOffset()) { - write = true; - } - } else { - write = true; - } - if (write) { - if (previousWritedChunkCount != lastSampleCount) { - stsc.getEntries().add(new SampleToChunkBox.Entry(lastChunkNumber, lastSampleCount, 1)); - previousWritedChunkCount = lastSampleCount; - } - lastSampleCount = 0; - lastChunkNumber++; - } - } - stbl.addBox(stsc); - } - - protected void createStsz(Track track, SampleTableBox stbl) { - SampleSizeBox stsz = new SampleSizeBox(); - stsz.setSampleSizes(track2SampleSizes.get(track)); - stbl.addBox(stsz); - } - - protected void createStco(Track track, SampleTableBox stbl) { - ArrayList<Long> chunksOffsets = new ArrayList<>(); - long lastOffset = -1; - for (Sample sample : track.getSamples()) { - long offset = sample.getOffset(); - if (lastOffset != -1 && lastOffset != offset) { - lastOffset = -1; - } - if (lastOffset == -1) { - chunksOffsets.add(offset); - } - lastOffset = offset + sample.getSize(); - } - long[] chunkOffsetsLong = new long[chunksOffsets.size()]; - for (int a = 0; a < chunksOffsets.size(); a++) { - chunkOffsetsLong[a] = chunksOffsets.get(a); - } - - StaticChunkOffsetBox stco = new StaticChunkOffsetBox(); - stco.setChunkOffsets(chunkOffsetsLong); - stbl.addBox(stco); - } -} diff --git a/src/main/java/de/pixart/messenger/utils/video/MediaController.java b/src/main/java/de/pixart/messenger/utils/video/MediaController.java deleted file mode 100644 index 2b47afade..000000000 --- a/src/main/java/de/pixart/messenger/utils/video/MediaController.java +++ /dev/null @@ -1,649 +0,0 @@ -package de.pixart.messenger.utils.video; - -import android.annotation.SuppressLint; -import android.annotation.TargetApi; -import android.media.MediaCodec; -import android.media.MediaCodecInfo; -import android.media.MediaCodecList; -import android.media.MediaExtractor; -import android.media.MediaFormat; -import android.media.MediaMetadataRetriever; -import android.os.Build; -import android.util.Log; - -import java.io.File; -import java.nio.ByteBuffer; - -import de.pixart.messenger.Config; - -@SuppressLint("NewApi") -public class MediaController { - - public final static String MIME_TYPE = "video/avc"; - private final static int PROCESSOR_TYPE_OTHER = 0; - private final static int PROCESSOR_TYPE_QCOM = 1; - private final static int PROCESSOR_TYPE_INTEL = 2; - private final static int PROCESSOR_TYPE_MTK = 3; - private final static int PROCESSOR_TYPE_SEC = 4; - private final static int PROCESSOR_TYPE_TI = 5; - private static volatile MediaController Instance = null; - private boolean videoConvertFirstWrite = true; - private int resultHeight = 0; - private int resultWidth = 0; - - public static MediaController getInstance() { - MediaController localInstance = Instance; - if (localInstance == null) { - synchronized (MediaController.class) { - localInstance = Instance; - if (localInstance == null) { - Instance = localInstance = new MediaController(); - } - } - } - return localInstance; - } - - @SuppressLint("NewApi") - public static int selectColorFormat(MediaCodecInfo codecInfo, String mimeType) { - MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType); - int lastColorFormat = 0; - for (int i = 0; i < capabilities.colorFormats.length; i++) { - int colorFormat = capabilities.colorFormats[i]; - if (isRecognizedFormat(colorFormat)) { - lastColorFormat = colorFormat; - if (!(codecInfo.getName().equals("OMX.SEC.AVC.Encoder") && colorFormat == 19)) { - return colorFormat; - } - } - } - return lastColorFormat; - } - - private static boolean isRecognizedFormat(int colorFormat) { - switch (colorFormat) { - case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar: - case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar: - case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar: - case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar: - case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar: - return true; - default: - return false; - } - } - - public native static int convertVideoFrame(ByteBuffer src, ByteBuffer dest, int destFormat, int width, int height, int padding, int swap); - - private void didWriteData(final boolean last, final boolean error) { - final boolean firstWrite = videoConvertFirstWrite; - if (firstWrite) { - videoConvertFirstWrite = false; - } - } - - public static MediaCodecInfo selectCodec(String mimeType) { - int numCodecs = MediaCodecList.getCodecCount(); - MediaCodecInfo lastCodecInfo = null; - for (int i = 0; i < numCodecs; i++) { - MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i); - if (!codecInfo.isEncoder()) { - continue; - } - String[] types = codecInfo.getSupportedTypes(); - for (String type : types) { - if (type.equalsIgnoreCase(mimeType)) { - lastCodecInfo = codecInfo; - if (!lastCodecInfo.getName().equals("OMX.SEC.avc.enc")) { - return lastCodecInfo; - } else if (lastCodecInfo.getName().equals("OMX.SEC.AVC.Encoder")) { - return lastCodecInfo; - } - } - } - } - return lastCodecInfo; - } - - @TargetApi(16) - private long readAndWriteTrack(MediaExtractor extractor, MP4Builder mediaMuxer, MediaCodec.BufferInfo info, long start, long end, File file, boolean isAudio) throws Exception { - int trackIndex = selectTrack(extractor, isAudio); - if (trackIndex >= 0) { - extractor.selectTrack(trackIndex); - MediaFormat trackFormat = extractor.getTrackFormat(trackIndex); - int muxerTrackIndex = mediaMuxer.addTrack(trackFormat, isAudio); - int maxBufferSize = trackFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE); - boolean inputDone = false; - if (start > 0) { - extractor.seekTo(start, MediaExtractor.SEEK_TO_PREVIOUS_SYNC); - } else { - extractor.seekTo(0, MediaExtractor.SEEK_TO_PREVIOUS_SYNC); - } - ByteBuffer buffer = ByteBuffer.allocateDirect(maxBufferSize); - long startTime = -1; - - while (!inputDone) { - - boolean eof = false; - int index = extractor.getSampleTrackIndex(); - if (index == trackIndex) { - info.size = extractor.readSampleData(buffer, 0); - - if (info.size < 0) { - info.size = 0; - eof = true; - } else { - info.presentationTimeUs = extractor.getSampleTime(); - if (start > 0 && startTime == -1) { - startTime = info.presentationTimeUs; - } - if (end < 0 || info.presentationTimeUs < end) { - info.offset = 0; - info.flags = extractor.getSampleFlags(); - if (mediaMuxer.writeSampleData(muxerTrackIndex, buffer, info, isAudio)) { - // didWriteData(messageObject, file, false, false); - } - extractor.advance(); - } else { - eof = true; - } - } - } else if (index == -1) { - eof = true; - } - if (eof) { - inputDone = true; - } - } - - extractor.unselectTrack(trackIndex); - return startTime; - } - return -1; - } - - @TargetApi(16) - private int selectTrack(MediaExtractor extractor, boolean audio) { - int numTracks = extractor.getTrackCount(); - for (int i = 0; i < numTracks; i++) { - MediaFormat format = extractor.getTrackFormat(i); - String mime = format.getString(MediaFormat.KEY_MIME); - if (audio) { - if (mime.startsWith("audio/")) { - return i; - } - } else { - if (mime.startsWith("video/")) { - return i; - } - } - } - return -5; - } - - @TargetApi(16) - public boolean convertVideo(final String path, final String compressed_path) { - - MediaMetadataRetriever retriever = new MediaMetadataRetriever(); - retriever.setDataSource(path); - String height = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT); - String width = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH); - String rotation = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION); - - int video_height = Integer.parseInt(height); - int video_width = Integer.parseInt(width); - - long startTime = -1; - long endTime = -1; - - int rotationValue = Integer.valueOf(rotation); - int originalWidth = Integer.valueOf(width); - int originalHeight = Integer.valueOf(height); - float ratio = (float) video_width / (float) video_height; // 16:9 = 1,7778, 4:3 = 1,3333 - - if (video_height > video_width) { - resultHeight = Config.VIDEO_SIZE; - resultWidth = Math.round(resultHeight * ratio); - } else if (video_width > video_height) { - resultWidth = Config.VIDEO_SIZE; - resultHeight = Math.round(resultWidth / ratio); - } - - if (resultHeight > originalHeight) { - resultHeight = originalHeight; - } - - if (resultWidth > originalWidth) { - resultWidth = originalWidth; - } - - Log.d(Config.LOGTAG, "Video dimensions: height: " + video_height + " width: " + video_width + " rotation: " + rotation + " ratio: " + ratio); - Log.d(Config.LOGTAG, "Video dimensions: Result height: " + resultHeight + " Result width: " + resultWidth + " rotation: " + rotation + " ratio: " + ratio); - - int bitrate = Config.VIDEO_BITRATE; - int rotateRender = 0; - - File cacheFile = new File(compressed_path); - - if (Build.VERSION.SDK_INT < 18 && resultHeight > resultWidth && resultWidth != originalWidth && resultHeight != originalHeight) { - int temp = resultHeight; - resultHeight = resultWidth; - resultWidth = temp; - rotationValue = 90; - rotateRender = 270; - } else if (Build.VERSION.SDK_INT > 20) { - if (rotationValue == 90) { - int temp = resultHeight; - resultHeight = resultWidth; - resultWidth = temp; - rotationValue = 0; - rotateRender = 270; - } else if (rotationValue == 180) { - rotateRender = 180; - rotationValue = 0; - } else if (rotationValue == 270) { - int temp = resultHeight; - resultHeight = resultWidth; - resultWidth = temp; - rotationValue = 0; - rotateRender = 90; - } - } - - - File inputFile = new File(path); - Log.d(Config.LOGTAG, "Input file is: " + inputFile.toString()); - if (!inputFile.canRead()) { - didWriteData(true, true); - Log.d(Config.LOGTAG, "Compression failed. Input file could not be read."); - return false; - } - - videoConvertFirstWrite = true; - boolean error = false; - long videoStartTime = startTime; - - long time = System.currentTimeMillis(); - - if (resultWidth != 0 && resultHeight != 0) { - MP4Builder mediaMuxer = null; - MediaExtractor extractor = null; - - try { - MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); - Mp4Movie movie = new Mp4Movie(); - movie.setCacheFile(cacheFile); - movie.setRotation(rotationValue); - movie.setSize(resultWidth, resultHeight); - mediaMuxer = new MP4Builder().createMovie(movie); - extractor = new MediaExtractor(); - extractor.setDataSource(inputFile.toString()); - - - if (resultWidth != originalWidth || resultHeight != originalHeight) { - int videoIndex; - videoIndex = selectTrack(extractor, false); - - if (videoIndex >= 0) { - MediaCodec decoder = null; - MediaCodec encoder = null; - InputSurface inputSurface = null; - OutputSurface outputSurface = null; - - try { - long videoTime = -1; - boolean outputDone = false; - boolean inputDone = false; - boolean decoderDone = false; - int swapUV = 0; - int videoTrackIndex = -5; - - int colorFormat; - int processorType = PROCESSOR_TYPE_OTHER; - String manufacturer = Build.MANUFACTURER.toLowerCase(); - if (Build.VERSION.SDK_INT < 18) { - MediaCodecInfo codecInfo = selectCodec(MIME_TYPE); - colorFormat = selectColorFormat(codecInfo, MIME_TYPE); - if (colorFormat == 0) { - throw new RuntimeException("no supported color format"); - } - String codecName = codecInfo.getName(); - if (codecName.contains("OMX.qcom.")) { - processorType = PROCESSOR_TYPE_QCOM; - if (Build.VERSION.SDK_INT == 16) { - if (manufacturer.equals("lge") || manufacturer.equals("nokia")) { - swapUV = 1; - } - } - } else if (codecName.contains("OMX.Intel.")) { - processorType = PROCESSOR_TYPE_INTEL; - } else if (codecName.equals("OMX.MTK.VIDEO.ENCODER.AVC")) { - processorType = PROCESSOR_TYPE_MTK; - } else if (codecName.equals("OMX.SEC.AVC.Encoder")) { - processorType = PROCESSOR_TYPE_SEC; - swapUV = 1; - } else if (codecName.equals("OMX.TI.DUCATI1.VIDEO.H264E")) { - processorType = PROCESSOR_TYPE_TI; - } - Log.d(Config.LOGTAG, "codec = " + codecInfo.getName() + " manufacturer = " + manufacturer + "device = " + Build.MODEL); - } else { - colorFormat = MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface; - } - Log.d(Config.LOGTAG, "colorFormat = " + colorFormat); - - int resultHeightAligned = resultHeight; - int padding = 0; - int bufferSize = resultWidth * resultHeight * 3 / 2; - if (processorType == PROCESSOR_TYPE_OTHER) { - if (resultHeight % 16 != 0) { - resultHeightAligned += (16 - (resultHeight % 16)); - padding = resultWidth * (resultHeightAligned - resultHeight); - bufferSize += padding * 5 / 4; - } - } else if (processorType == PROCESSOR_TYPE_QCOM) { - if (!manufacturer.toLowerCase().equals("lge")) { - int uvoffset = (resultWidth * resultHeight + 2047) & ~2047; - padding = uvoffset - (resultWidth * resultHeight); - bufferSize += padding; - } - } else if (processorType == PROCESSOR_TYPE_TI) { - resultHeightAligned = 368; - bufferSize = resultWidth * resultHeightAligned * 3 / 2; - resultHeightAligned += (16 - (resultHeight % 16)); - padding = resultWidth * (resultHeightAligned - resultHeight); - bufferSize += padding * 5 / 4; - } else if (processorType == PROCESSOR_TYPE_MTK) { - if (manufacturer.equals("baidu")) { - resultHeightAligned += (16 - (resultHeight % 16)); - padding = resultWidth * (resultHeightAligned - resultHeight); - bufferSize += padding * 5 / 4; - } - } - - extractor.selectTrack(videoIndex); - if (startTime > 0) { - extractor.seekTo(startTime, MediaExtractor.SEEK_TO_PREVIOUS_SYNC); - } else { - extractor.seekTo(0, MediaExtractor.SEEK_TO_PREVIOUS_SYNC); - } - MediaFormat inputFormat = extractor.getTrackFormat(videoIndex); - - MediaFormat outputFormat = MediaFormat.createVideoFormat(MIME_TYPE, resultWidth, resultHeight); - outputFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat); - outputFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate != 0 ? bitrate : 921600); - outputFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 25); - outputFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10); - if (Build.VERSION.SDK_INT < 18) { - outputFormat.setInteger("stride", resultWidth + 32); - outputFormat.setInteger("slice-height", resultHeight); - } - - encoder = MediaCodec.createEncoderByType(MIME_TYPE); - encoder.configure(outputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); - if (Build.VERSION.SDK_INT >= 18) { - inputSurface = new InputSurface(encoder.createInputSurface()); - inputSurface.makeCurrent(); - } - encoder.start(); - - decoder = MediaCodec.createDecoderByType(inputFormat.getString(MediaFormat.KEY_MIME)); - if (Build.VERSION.SDK_INT >= 18) { - outputSurface = new OutputSurface(); - } else { - outputSurface = new OutputSurface(resultWidth, resultHeight, rotateRender); - } - decoder.configure(inputFormat, outputSurface.getSurface(), null, 0); - decoder.start(); - - final int TIMEOUT_USEC = 2500; - ByteBuffer[] decoderInputBuffers = null; - ByteBuffer[] encoderOutputBuffers = null; - ByteBuffer[] encoderInputBuffers = null; - if (Build.VERSION.SDK_INT < 21) { - decoderInputBuffers = decoder.getInputBuffers(); - encoderOutputBuffers = encoder.getOutputBuffers(); - if (Build.VERSION.SDK_INT < 18) { - encoderInputBuffers = encoder.getInputBuffers(); - } - } - - while (!outputDone) { - if (!inputDone) { - boolean eof = false; - int index = extractor.getSampleTrackIndex(); - if (index == videoIndex) { - int inputBufIndex = decoder.dequeueInputBuffer(TIMEOUT_USEC); - if (inputBufIndex >= 0) { - ByteBuffer inputBuf; - if (Build.VERSION.SDK_INT < 21) { - inputBuf = decoderInputBuffers[inputBufIndex]; - } else { - inputBuf = decoder.getInputBuffer(inputBufIndex); - } - int chunkSize = extractor.readSampleData(inputBuf, 0); - if (chunkSize < 0) { - decoder.queueInputBuffer(inputBufIndex, 0, 0, 0L, MediaCodec.BUFFER_FLAG_END_OF_STREAM); - inputDone = true; - } else { - decoder.queueInputBuffer(inputBufIndex, 0, chunkSize, extractor.getSampleTime(), 0); - extractor.advance(); - } - } - } else if (index == -1) { - eof = true; - } - if (eof) { - int inputBufIndex = decoder.dequeueInputBuffer(TIMEOUT_USEC); - if (inputBufIndex >= 0) { - decoder.queueInputBuffer(inputBufIndex, 0, 0, 0L, MediaCodec.BUFFER_FLAG_END_OF_STREAM); - inputDone = true; - } - } - } - - boolean decoderOutputAvailable = !decoderDone; - boolean encoderOutputAvailable = true; - while (decoderOutputAvailable || encoderOutputAvailable) { - int encoderStatus = encoder.dequeueOutputBuffer(info, TIMEOUT_USEC); - if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) { - encoderOutputAvailable = false; - } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { - if (Build.VERSION.SDK_INT < 21) { - encoderOutputBuffers = encoder.getOutputBuffers(); - } - } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { - MediaFormat newFormat = encoder.getOutputFormat(); - if (videoTrackIndex == -5) { - videoTrackIndex = mediaMuxer.addTrack(newFormat, false); - } - } else if (encoderStatus < 0) { - throw new RuntimeException("unexpected result from encoder.dequeueOutputBuffer: " + encoderStatus); - } else { - ByteBuffer encodedData; - if (Build.VERSION.SDK_INT < 21) { - encodedData = encoderOutputBuffers[encoderStatus]; - } else { - encodedData = encoder.getOutputBuffer(encoderStatus); - } - if (encodedData == null) { - throw new RuntimeException("encoderOutputBuffer " + encoderStatus + " was null"); - } - if (info.size > 1) { - if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) { - if (mediaMuxer.writeSampleData(videoTrackIndex, encodedData, info, false)) { - didWriteData(false, false); - } - } else if (videoTrackIndex == -5) { - byte[] csd = new byte[info.size]; - encodedData.limit(info.offset + info.size); - encodedData.position(info.offset); - encodedData.get(csd); - ByteBuffer sps = null; - ByteBuffer pps = null; - for (int a = info.size - 1; a >= 0; a--) { - if (a > 3) { - if (csd[a] == 1 && csd[a - 1] == 0 && csd[a - 2] == 0 && csd[a - 3] == 0) { - sps = ByteBuffer.allocate(a - 3); - pps = ByteBuffer.allocate(info.size - (a - 3)); - sps.put(csd, 0, a - 3).position(0); - pps.put(csd, a - 3, info.size - (a - 3)).position(0); - break; - } - } else { - break; - } - } - - MediaFormat newFormat = MediaFormat.createVideoFormat(MIME_TYPE, resultWidth, resultHeight); - if (sps != null && pps != null) { - newFormat.setByteBuffer("csd-0", sps); - newFormat.setByteBuffer("csd-1", pps); - } - videoTrackIndex = mediaMuxer.addTrack(newFormat, false); - } - } - outputDone = (info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0; - encoder.releaseOutputBuffer(encoderStatus, false); - } - if (encoderStatus != MediaCodec.INFO_TRY_AGAIN_LATER) { - continue; - } - - if (!decoderDone) { - int decoderStatus = decoder.dequeueOutputBuffer(info, TIMEOUT_USEC); - if (decoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) { - decoderOutputAvailable = false; - } else if (decoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { - - } else if (decoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { - MediaFormat newFormat = decoder.getOutputFormat(); - Log.d(Config.LOGTAG, "newFormat = " + newFormat); - } else if (decoderStatus < 0) { - throw new RuntimeException("unexpected result from decoder.dequeueOutputBuffer: " + decoderStatus); - } else { - boolean doRender; - if (Build.VERSION.SDK_INT >= 18) { - doRender = info.size != 0; - } else { - doRender = info.size != 0 || info.presentationTimeUs != 0; - } - if (endTime > 0 && info.presentationTimeUs >= endTime) { - inputDone = true; - decoderDone = true; - doRender = false; - info.flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM; - } - if (startTime > 0 && videoTime == -1) { - if (info.presentationTimeUs < startTime) { - doRender = false; - Log.d(Config.LOGTAG, "drop frame startTime = " + startTime + " present time = " + info.presentationTimeUs); - } else { - videoTime = info.presentationTimeUs; - } - } - decoder.releaseOutputBuffer(decoderStatus, doRender); - if (doRender) { - boolean errorWait = false; - try { - outputSurface.awaitNewImage(); - } catch (Exception e) { - errorWait = true; - Log.d(Config.LOGTAG, e.getMessage()); - } - if (!errorWait) { - if (Build.VERSION.SDK_INT >= 18) { - outputSurface.drawImage(false); - inputSurface.setPresentationTime(info.presentationTimeUs * 1000); - inputSurface.swapBuffers(); - } else { - int inputBufIndex = encoder.dequeueInputBuffer(TIMEOUT_USEC); - if (inputBufIndex >= 0) { - outputSurface.drawImage(true); - ByteBuffer rgbBuf = outputSurface.getFrame(); - ByteBuffer yuvBuf = encoderInputBuffers[inputBufIndex]; - yuvBuf.clear(); - convertVideoFrame(rgbBuf, yuvBuf, colorFormat, resultWidth, resultHeight, padding, swapUV); - encoder.queueInputBuffer(inputBufIndex, 0, bufferSize, info.presentationTimeUs, 0); - } else { - Log.d(Config.LOGTAG, "input buffer not available"); - } - } - } - } - if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { - decoderOutputAvailable = false; - Log.d(Config.LOGTAG, "decoder stream end"); - if (Build.VERSION.SDK_INT >= 18) { - encoder.signalEndOfInputStream(); - } else { - int inputBufIndex = encoder.dequeueInputBuffer(TIMEOUT_USEC); - if (inputBufIndex >= 0) { - encoder.queueInputBuffer(inputBufIndex, 0, 1, info.presentationTimeUs, MediaCodec.BUFFER_FLAG_END_OF_STREAM); - } - } - } - } - } - } - } - if (videoTime != -1) { - videoStartTime = videoTime; - } - } catch (Exception e) { - Log.d(Config.LOGTAG, e.getMessage()); - error = true; - } - - extractor.unselectTrack(videoIndex); - - if (outputSurface != null) { - outputSurface.release(); - } - if (inputSurface != null) { - inputSurface.release(); - } - if (decoder != null) { - decoder.stop(); - decoder.release(); - } - if (encoder != null) { - encoder.stop(); - encoder.release(); - } - } - } else { - long videoTime = readAndWriteTrack(extractor, mediaMuxer, info, startTime, endTime, cacheFile, false); - if (videoTime != -1) { - videoStartTime = videoTime; - } - } - if (!error) { - readAndWriteTrack(extractor, mediaMuxer, info, videoStartTime, endTime, cacheFile, true); - } - } catch (Exception e) { - error = true; - Log.d(Config.LOGTAG, e.getMessage()); - } finally { - if (extractor != null) { - extractor.release(); - } - if (mediaMuxer != null) { - try { - mediaMuxer.finishMovie(false); - } catch (Exception e) { - Log.d(Config.LOGTAG, e.getMessage()); - } - } - Log.d(Config.LOGTAG, "time = " + (System.currentTimeMillis() - time)); - } - } else { - didWriteData(true, true); - Log.d(Config.LOGTAG, "Compression failed."); - return false; - } - didWriteData(true, error); - Log.d(Config.LOGTAG, "Compression succeed. Save compressed video to " + cacheFile.toString()); - //inputFile.delete(); - return true; - } -}
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/utils/video/Mp4Movie.java b/src/main/java/de/pixart/messenger/utils/video/Mp4Movie.java deleted file mode 100644 index 4cf206d26..000000000 --- a/src/main/java/de/pixart/messenger/utils/video/Mp4Movie.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * This is the source code of Telegram for Android v. 1.7.x. - * It is licensed under GNU GPL v. 2 or later. - * You should have received a copy of the license in this archive (see LICENSE). - * - * Copyright Nikolai Kudashov, 2013-2014. - */ - -package de.pixart.messenger.utils.video; - -import android.annotation.TargetApi; -import android.media.MediaCodec; -import android.media.MediaFormat; - -import com.googlecode.mp4parser.util.Matrix; - -import java.io.File; -import java.util.ArrayList; - -@TargetApi(16) -public class Mp4Movie { - private Matrix matrix = Matrix.ROTATE_0; - private ArrayList<Track> tracks = new ArrayList<Track>(); - private File cacheFile; - private int width; - private int height; - - public Matrix getMatrix() { - return matrix; - } - - public int getWidth() { - return width; - } - - public int getHeight() { - return height; - } - - public void setCacheFile(File file) { - cacheFile = file; - } - - public void setRotation(int angle) { - if (angle == 0) { - matrix = Matrix.ROTATE_0; - } else if (angle == 90) { - matrix = Matrix.ROTATE_90; - } else if (angle == 180) { - matrix = Matrix.ROTATE_180; - } else if (angle == 270) { - matrix = Matrix.ROTATE_270; - } - } - - public void setSize(int w, int h) { - width = w; - height = h; - } - - public ArrayList<Track> getTracks() { - return tracks; - } - - public File getCacheFile() { - return cacheFile; - } - - public void addSample(int trackIndex, long offset, MediaCodec.BufferInfo bufferInfo) throws Exception { - if (trackIndex < 0 || trackIndex >= tracks.size()) { - return; - } - Track track = tracks.get(trackIndex); - track.addSample(offset, bufferInfo); - } - - public int addTrack(MediaFormat mediaFormat, boolean isAudio) throws Exception { - tracks.add(new Track(tracks.size(), mediaFormat, isAudio)); - return tracks.size() - 1; - } -} diff --git a/src/main/java/de/pixart/messenger/utils/video/OutputSurface.java b/src/main/java/de/pixart/messenger/utils/video/OutputSurface.java deleted file mode 100644 index 75ceb46ac..000000000 --- a/src/main/java/de/pixart/messenger/utils/video/OutputSurface.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.pixart.messenger.utils.video; - -import android.annotation.TargetApi; -import android.graphics.SurfaceTexture; -import android.opengl.GLES20; -import android.view.Surface; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -import javax.microedition.khronos.egl.EGL10; -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.egl.EGLContext; -import javax.microedition.khronos.egl.EGLDisplay; -import javax.microedition.khronos.egl.EGLSurface; - -@TargetApi(16) -public class OutputSurface implements SurfaceTexture.OnFrameAvailableListener { - - private static final int EGL_OPENGL_ES2_BIT = 4; - private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; - private EGL10 mEGL; - private EGLDisplay mEGLDisplay = null; - private EGLContext mEGLContext = null; - private EGLSurface mEGLSurface = null; - private SurfaceTexture mSurfaceTexture; - private Surface mSurface; - private final Object mFrameSyncObject = new Object(); - private boolean mFrameAvailable; - private TextureRenderer mTextureRender; - private int mWidth; - private int mHeight; - private int rotateRender = 0; - private ByteBuffer mPixelBuf; - - public OutputSurface(int width, int height, int rotate) { - if (width <= 0 || height <= 0) { - throw new IllegalArgumentException(); - } - mWidth = width; - mHeight = height; - rotateRender = rotate; - mPixelBuf = ByteBuffer.allocateDirect(mWidth * mHeight * 4); - mPixelBuf.order(ByteOrder.LITTLE_ENDIAN); - eglSetup(width, height); - makeCurrent(); - setup(); - } - - public OutputSurface() { - setup(); - } - - private void setup() { - mTextureRender = new TextureRenderer(rotateRender); - mTextureRender.surfaceCreated(); - mSurfaceTexture = new SurfaceTexture(mTextureRender.getTextureId()); - mSurfaceTexture.setOnFrameAvailableListener(this); - mSurface = new Surface(mSurfaceTexture); - } - - private void eglSetup(int width, int height) { - mEGL = (EGL10) EGLContext.getEGL(); - mEGLDisplay = mEGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); - - if (mEGLDisplay == EGL10.EGL_NO_DISPLAY) { - throw new RuntimeException("unable to get EGL10 display"); - } - - if (!mEGL.eglInitialize(mEGLDisplay, null)) { - mEGLDisplay = null; - throw new RuntimeException("unable to initialize EGL10"); - } - - int[] attribList = { - EGL10.EGL_RED_SIZE, 8, - EGL10.EGL_GREEN_SIZE, 8, - EGL10.EGL_BLUE_SIZE, 8, - EGL10.EGL_ALPHA_SIZE, 8, - EGL10.EGL_SURFACE_TYPE, EGL10.EGL_PBUFFER_BIT, - EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL10.EGL_NONE - }; - EGLConfig[] configs = new EGLConfig[1]; - int[] numConfigs = new int[1]; - if (!mEGL.eglChooseConfig(mEGLDisplay, attribList, configs, configs.length, numConfigs)) { - throw new RuntimeException("unable to find RGB888+pbuffer EGL config"); - } - int[] attrib_list = { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL10.EGL_NONE - }; - mEGLContext = mEGL.eglCreateContext(mEGLDisplay, configs[0], EGL10.EGL_NO_CONTEXT, attrib_list); - checkEglError("eglCreateContext"); - if (mEGLContext == null) { - throw new RuntimeException("null context"); - } - int[] surfaceAttribs = { - EGL10.EGL_WIDTH, width, - EGL10.EGL_HEIGHT, height, - EGL10.EGL_NONE - }; - mEGLSurface = mEGL.eglCreatePbufferSurface(mEGLDisplay, configs[0], surfaceAttribs); - checkEglError("eglCreatePbufferSurface"); - if (mEGLSurface == null) { - throw new RuntimeException("surface was null"); - } - } - - public void release() { - if (mEGL != null) { - if (mEGL.eglGetCurrentContext().equals(mEGLContext)) { - mEGL.eglMakeCurrent(mEGLDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); - } - mEGL.eglDestroySurface(mEGLDisplay, mEGLSurface); - mEGL.eglDestroyContext(mEGLDisplay, mEGLContext); - } - mSurface.release(); - mEGLDisplay = null; - mEGLContext = null; - mEGLSurface = null; - mEGL = null; - mTextureRender = null; - mSurface = null; - mSurfaceTexture = null; - } - - public void makeCurrent() { - if (mEGL == null) { - throw new RuntimeException("not configured for makeCurrent"); - } - checkEglError("before makeCurrent"); - if (!mEGL.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) { - throw new RuntimeException("eglMakeCurrent failed"); - } - } - - public Surface getSurface() { - return mSurface; - } - - public void changeFragmentShader(String fragmentShader) { - mTextureRender.changeFragmentShader(fragmentShader); - } - - public void awaitNewImage() { - final int TIMEOUT_MS = 5000; - synchronized (mFrameSyncObject) { - while (!mFrameAvailable) { - try { - mFrameSyncObject.wait(TIMEOUT_MS); - if (!mFrameAvailable) { - throw new RuntimeException("Surface frame wait timed out"); - } - } catch (InterruptedException ie) { - throw new RuntimeException(ie); - } - } - mFrameAvailable = false; - } - mTextureRender.checkGlError("before updateTexImage"); - mSurfaceTexture.updateTexImage(); - } - - public void drawImage(boolean invert) { - mTextureRender.drawFrame(mSurfaceTexture, invert); - } - - @Override - public void onFrameAvailable(SurfaceTexture st) { - synchronized (mFrameSyncObject) { - if (mFrameAvailable) { - throw new RuntimeException("mFrameAvailable already set, frame could be dropped"); - } - mFrameAvailable = true; - mFrameSyncObject.notifyAll(); - } - } - - public ByteBuffer getFrame() { - mPixelBuf.rewind(); - GLES20.glReadPixels(0, 0, mWidth, mHeight, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mPixelBuf); - return mPixelBuf; - } - - private void checkEglError(String msg) { - if (mEGL.eglGetError() != EGL10.EGL_SUCCESS) { - throw new RuntimeException("EGL error encountered (see log)"); - } - } -} diff --git a/src/main/java/de/pixart/messenger/utils/video/Sample.java b/src/main/java/de/pixart/messenger/utils/video/Sample.java deleted file mode 100644 index f00ee5311..000000000 --- a/src/main/java/de/pixart/messenger/utils/video/Sample.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * This is the source code of Telegram for Android v. 1.7.x. - * It is licensed under GNU GPL v. 2 or later. - * You should have received a copy of the license in this archive (see LICENSE). - * - * Copyright Nikolai Kudashov, 2013-2014. - */ - -package de.pixart.messenger.utils.video; - -public class Sample { - private long offset = 0; - private long size = 0; - - public Sample(long offset, long size) { - this.offset = offset; - this.size = size; - } - - public long getOffset() { - return offset; - } - - public long getSize() { - return size; - } -} diff --git a/src/main/java/de/pixart/messenger/utils/video/TextureRenderer.java b/src/main/java/de/pixart/messenger/utils/video/TextureRenderer.java deleted file mode 100644 index 3c53941ea..000000000 --- a/src/main/java/de/pixart/messenger/utils/video/TextureRenderer.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.pixart.messenger.utils.video; - -import android.annotation.TargetApi; -import android.graphics.SurfaceTexture; -import android.opengl.GLES11Ext; -import android.opengl.GLES20; -import android.opengl.Matrix; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; - -@TargetApi(16) -public class TextureRenderer { - - private static final int FLOAT_SIZE_BYTES = 4; - private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES; - private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0; - private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3; - private static final float[] mTriangleVerticesData = { - -1.0f, -1.0f, 0, 0.f, 0.f, - 1.0f, -1.0f, 0, 1.f, 0.f, - -1.0f, 1.0f, 0, 0.f, 1.f, - 1.0f, 1.0f, 0, 1.f, 1.f, - }; - private FloatBuffer mTriangleVertices; - - private static final String VERTEX_SHADER = - "uniform mat4 uMVPMatrix;\n" + - "uniform mat4 uSTMatrix;\n" + - "attribute vec4 aPosition;\n" + - "attribute vec4 aTextureCoord;\n" + - "varying vec2 vTextureCoord;\n" + - "void main() {\n" + - " gl_Position = uMVPMatrix * aPosition;\n" + - " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" + - "}\n"; - - private static final String FRAGMENT_SHADER = - "#extension GL_OES_EGL_image_external : require\n" + - "precision mediump float;\n" + - "varying vec2 vTextureCoord;\n" + - "uniform samplerExternalOES sTexture;\n" + - "void main() {\n" + - " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" + - "}\n"; - - private float[] mMVPMatrix = new float[16]; - private float[] mSTMatrix = new float[16]; - private int mProgram; - private int mTextureID = -12345; - private int muMVPMatrixHandle; - private int muSTMatrixHandle; - private int maPositionHandle; - private int maTextureHandle; - private int rotationAngle = 0; - - public TextureRenderer(int rotation) { - rotationAngle = rotation; - mTriangleVertices = ByteBuffer.allocateDirect(mTriangleVerticesData.length * FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer(); - mTriangleVertices.put(mTriangleVerticesData).position(0); - Matrix.setIdentityM(mSTMatrix, 0); - } - - public int getTextureId() { - return mTextureID; - } - - public void drawFrame(SurfaceTexture st, boolean invert) { - checkGlError("onDrawFrame start"); - st.getTransformMatrix(mSTMatrix); - - if (invert) { - mSTMatrix[5] = -mSTMatrix[5]; - mSTMatrix[13] = 1.0f - mSTMatrix[13]; - } - - GLES20.glUseProgram(mProgram); - checkGlError("glUseProgram"); - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID); - mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET); - GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices); - checkGlError("glVertexAttribPointer maPosition"); - GLES20.glEnableVertexAttribArray(maPositionHandle); - checkGlError("glEnableVertexAttribArray maPositionHandle"); - mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET); - GLES20.glVertexAttribPointer(maTextureHandle, 2, GLES20.GL_FLOAT, false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices); - checkGlError("glVertexAttribPointer maTextureHandle"); - GLES20.glEnableVertexAttribArray(maTextureHandle); - checkGlError("glEnableVertexAttribArray maTextureHandle"); - GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0); - GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0); - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); - checkGlError("glDrawArrays"); - GLES20.glFinish(); - } - - public void surfaceCreated() { - mProgram = createProgram(VERTEX_SHADER, FRAGMENT_SHADER); - if (mProgram == 0) { - throw new RuntimeException("failed creating program"); - } - maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition"); - checkGlError("glGetAttribLocation aPosition"); - if (maPositionHandle == -1) { - throw new RuntimeException("Could not get attrib location for aPosition"); - } - maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord"); - checkGlError("glGetAttribLocation aTextureCoord"); - if (maTextureHandle == -1) { - throw new RuntimeException("Could not get attrib location for aTextureCoord"); - } - muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); - checkGlError("glGetUniformLocation uMVPMatrix"); - if (muMVPMatrixHandle == -1) { - throw new RuntimeException("Could not get attrib location for uMVPMatrix"); - } - muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix"); - checkGlError("glGetUniformLocation uSTMatrix"); - if (muSTMatrixHandle == -1) { - throw new RuntimeException("Could not get attrib location for uSTMatrix"); - } - int[] textures = new int[1]; - GLES20.glGenTextures(1, textures, 0); - mTextureID = textures[0]; - GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID); - checkGlError("glBindTexture mTextureID"); - GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); - GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); - GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); - GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); - checkGlError("glTexParameter"); - - Matrix.setIdentityM(mMVPMatrix, 0); - if (rotationAngle != 0) { - Matrix.rotateM(mMVPMatrix, 0, rotationAngle, 0, 0, 1); - } - } - - public void changeFragmentShader(String fragmentShader) { - GLES20.glDeleteProgram(mProgram); - mProgram = createProgram(VERTEX_SHADER, fragmentShader); - if (mProgram == 0) { - throw new RuntimeException("failed creating program"); - } - } - - private int loadShader(int shaderType, String source) { - int shader = GLES20.glCreateShader(shaderType); - checkGlError("glCreateShader type=" + shaderType); - GLES20.glShaderSource(shader, source); - GLES20.glCompileShader(shader); - int[] compiled = new int[1]; - GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); - if (compiled[0] == 0) { - GLES20.glDeleteShader(shader); - shader = 0; - } - return shader; - } - - private int createProgram(String vertexSource, String fragmentSource) { - int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource); - if (vertexShader == 0) { - return 0; - } - int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); - if (pixelShader == 0) { - return 0; - } - int program = GLES20.glCreateProgram(); - checkGlError("glCreateProgram"); - if (program == 0) { - return 0; - } - GLES20.glAttachShader(program, vertexShader); - checkGlError("glAttachShader"); - GLES20.glAttachShader(program, pixelShader); - checkGlError("glAttachShader"); - GLES20.glLinkProgram(program); - int[] linkStatus = new int[1]; - GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); - if (linkStatus[0] != GLES20.GL_TRUE) { - GLES20.glDeleteProgram(program); - program = 0; - } - return program; - } - - public void checkGlError(String op) { - int error; - if ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { - throw new RuntimeException(op + ": glError " + error); - } - } -} diff --git a/src/main/java/de/pixart/messenger/utils/video/Track.java b/src/main/java/de/pixart/messenger/utils/video/Track.java deleted file mode 100644 index 18111a675..000000000 --- a/src/main/java/de/pixart/messenger/utils/video/Track.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * This is the source code of Telegram for Android v. 1.7.x. - * It is licensed under GNU GPL v. 2 or later. - * You should have received a copy of the license in this archive (see LICENSE). - * - * Copyright Nikolai Kudashov, 2013-2014. - */ - -package de.pixart.messenger.utils.video; - -import android.annotation.TargetApi; -import android.media.MediaCodec; -import android.media.MediaFormat; - -import com.coremedia.iso.boxes.AbstractMediaHeaderBox; -import com.coremedia.iso.boxes.SampleDescriptionBox; -import com.coremedia.iso.boxes.SoundMediaHeaderBox; -import com.coremedia.iso.boxes.VideoMediaHeaderBox; -import com.coremedia.iso.boxes.sampleentry.AudioSampleEntry; -import com.coremedia.iso.boxes.sampleentry.VisualSampleEntry; -import com.googlecode.mp4parser.boxes.mp4.ESDescriptorBox; -import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.AudioSpecificConfig; -import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.DecoderConfigDescriptor; -import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.ESDescriptor; -import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.SLConfigDescriptor; -import com.mp4parser.iso14496.part15.AvcConfigurationBox; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.Map; - -@TargetApi(16) -public class Track { - private long trackId = 0; - private ArrayList<Sample> samples = new ArrayList<Sample>(); - private long duration = 0; - private String handler; - private AbstractMediaHeaderBox headerBox = null; - private SampleDescriptionBox sampleDescriptionBox = null; - private LinkedList<Integer> syncSamples = null; - private int timeScale; - private Date creationTime = new Date(); - private int height; - private int width; - private float volume = 0; - private ArrayList<Long> sampleDurations = new ArrayList<Long>(); - private boolean isAudio = false; - private static Map<Integer, Integer> samplingFrequencyIndexMap = new HashMap<Integer, Integer>(); - private long lastPresentationTimeUs = 0; - private boolean first = true; - - static { - samplingFrequencyIndexMap.put(96000, 0x0); - samplingFrequencyIndexMap.put(88200, 0x1); - samplingFrequencyIndexMap.put(64000, 0x2); - samplingFrequencyIndexMap.put(48000, 0x3); - samplingFrequencyIndexMap.put(44100, 0x4); - samplingFrequencyIndexMap.put(32000, 0x5); - samplingFrequencyIndexMap.put(24000, 0x6); - samplingFrequencyIndexMap.put(22050, 0x7); - samplingFrequencyIndexMap.put(16000, 0x8); - samplingFrequencyIndexMap.put(12000, 0x9); - samplingFrequencyIndexMap.put(11025, 0xa); - samplingFrequencyIndexMap.put(8000, 0xb); - } - - public Track(int id, MediaFormat format, boolean isAudio) throws Exception { - trackId = id; - if (!isAudio) { - sampleDurations.add((long) 3015); - duration = 3015; - width = format.getInteger(MediaFormat.KEY_WIDTH); - height = format.getInteger(MediaFormat.KEY_HEIGHT); - timeScale = 90000; - syncSamples = new LinkedList<Integer>(); - handler = "vide"; - headerBox = new VideoMediaHeaderBox(); - sampleDescriptionBox = new SampleDescriptionBox(); - String mime = format.getString(MediaFormat.KEY_MIME); - if (mime.equals("video/avc")) { - VisualSampleEntry visualSampleEntry = new VisualSampleEntry("avc1"); - visualSampleEntry.setDataReferenceIndex(1); - visualSampleEntry.setDepth(24); - visualSampleEntry.setFrameCount(1); - visualSampleEntry.setHorizresolution(72); - visualSampleEntry.setVertresolution(72); - visualSampleEntry.setWidth(width); - visualSampleEntry.setHeight(height); - - AvcConfigurationBox avcConfigurationBox = new AvcConfigurationBox(); - - if (format.getByteBuffer("csd-0") != null) { - ArrayList<byte[]> spsArray = new ArrayList<byte[]>(); - ByteBuffer spsBuff = format.getByteBuffer("csd-0"); - spsBuff.position(4); - byte[] spsBytes = new byte[spsBuff.remaining()]; - spsBuff.get(spsBytes); - spsArray.add(spsBytes); - - ArrayList<byte[]> ppsArray = new ArrayList<byte[]>(); - ByteBuffer ppsBuff = format.getByteBuffer("csd-1"); - ppsBuff.position(4); - byte[] ppsBytes = new byte[ppsBuff.remaining()]; - ppsBuff.get(ppsBytes); - ppsArray.add(ppsBytes); - avcConfigurationBox.setSequenceParameterSets(spsArray); - avcConfigurationBox.setPictureParameterSets(ppsArray); - } - //ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(spsBytes); - //SeqParameterSet seqParameterSet = SeqParameterSet.read(byteArrayInputStream); - - avcConfigurationBox.setAvcLevelIndication(13); - avcConfigurationBox.setAvcProfileIndication(100); - avcConfigurationBox.setBitDepthLumaMinus8(-1); - avcConfigurationBox.setBitDepthChromaMinus8(-1); - avcConfigurationBox.setChromaFormat(-1); - avcConfigurationBox.setConfigurationVersion(1); - avcConfigurationBox.setLengthSizeMinusOne(3); - avcConfigurationBox.setProfileCompatibility(0); - - visualSampleEntry.addBox(avcConfigurationBox); - sampleDescriptionBox.addBox(visualSampleEntry); - } else if (mime.equals("video/mp4v")) { - VisualSampleEntry visualSampleEntry = new VisualSampleEntry("mp4v"); - visualSampleEntry.setDataReferenceIndex(1); - visualSampleEntry.setDepth(24); - visualSampleEntry.setFrameCount(1); - visualSampleEntry.setHorizresolution(72); - visualSampleEntry.setVertresolution(72); - visualSampleEntry.setWidth(width); - visualSampleEntry.setHeight(height); - - sampleDescriptionBox.addBox(visualSampleEntry); - } - } else { - sampleDurations.add((long) 1024); - duration = 1024; - isAudio = true; - volume = 1; - timeScale = format.getInteger(MediaFormat.KEY_SAMPLE_RATE); - handler = "soun"; - headerBox = new SoundMediaHeaderBox(); - sampleDescriptionBox = new SampleDescriptionBox(); - AudioSampleEntry audioSampleEntry = new AudioSampleEntry("mp4a"); - audioSampleEntry.setChannelCount(format.getInteger(MediaFormat.KEY_CHANNEL_COUNT)); - audioSampleEntry.setSampleRate(format.getInteger(MediaFormat.KEY_SAMPLE_RATE)); - audioSampleEntry.setDataReferenceIndex(1); - audioSampleEntry.setSampleSize(16); - - ESDescriptorBox esds = new ESDescriptorBox(); - ESDescriptor descriptor = new ESDescriptor(); - descriptor.setEsId(0); - - SLConfigDescriptor slConfigDescriptor = new SLConfigDescriptor(); - slConfigDescriptor.setPredefined(2); - descriptor.setSlConfigDescriptor(slConfigDescriptor); - - DecoderConfigDescriptor decoderConfigDescriptor = new DecoderConfigDescriptor(); - decoderConfigDescriptor.setObjectTypeIndication(0x40); - decoderConfigDescriptor.setStreamType(5); - decoderConfigDescriptor.setBufferSizeDB(1536); - decoderConfigDescriptor.setMaxBitRate(96000); - decoderConfigDescriptor.setAvgBitRate(96000); - - AudioSpecificConfig audioSpecificConfig = new AudioSpecificConfig(); - audioSpecificConfig.setAudioObjectType(2); - audioSpecificConfig.setSamplingFrequencyIndex(samplingFrequencyIndexMap.get((int) audioSampleEntry.getSampleRate())); - audioSpecificConfig.setChannelConfiguration(audioSampleEntry.getChannelCount()); - decoderConfigDescriptor.setAudioSpecificInfo(audioSpecificConfig); - - descriptor.setDecoderConfigDescriptor(decoderConfigDescriptor); - - ByteBuffer data = descriptor.serialize(); - esds.setEsDescriptor(descriptor); - esds.setData(data); - audioSampleEntry.addBox(esds); - sampleDescriptionBox.addBox(audioSampleEntry); - } - } - - public long getTrackId() { - return trackId; - } - - public void addSample(long offset, MediaCodec.BufferInfo bufferInfo) { - boolean isSyncFrame = !isAudio && (bufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0; - samples.add(new Sample(offset, bufferInfo.size)); - if (syncSamples != null && isSyncFrame) { - syncSamples.add(samples.size()); - } - - long delta = bufferInfo.presentationTimeUs - lastPresentationTimeUs; - lastPresentationTimeUs = bufferInfo.presentationTimeUs; - delta = (delta * timeScale + 500000L) / 1000000L; - if (!first) { - sampleDurations.add(sampleDurations.size() - 1, delta); - duration += delta; - } - first = false; - } - - public ArrayList<Sample> getSamples() { - return samples; - } - - public long getDuration() { - return duration; - } - - public String getHandler() { - return handler; - } - - public AbstractMediaHeaderBox getMediaHeaderBox() { - return headerBox; - } - - public SampleDescriptionBox getSampleDescriptionBox() { - return sampleDescriptionBox; - } - - public long[] getSyncSamples() { - if (syncSamples == null || syncSamples.isEmpty()) { - return null; - } - long[] returns = new long[syncSamples.size()]; - for (int i = 0; i < syncSamples.size(); i++) { - returns[i] = syncSamples.get(i); - } - return returns; - } - - public int getTimeScale() { - return timeScale; - } - - public Date getCreationTime() { - return creationTime; - } - - public int getWidth() { - return width; - } - - public int getHeight() { - return height; - } - - public float getVolume() { - return volume; - } - - public ArrayList<Long> getSampleDurations() { - return sampleDurations; - } - - public boolean isAudio() { - return isAudio; - } -} diff --git a/src/main/res/layout/conversation_list_row.xml b/src/main/res/layout/conversation_list_row.xml index ec4b55363..08d938ffc 100644 --- a/src/main/res/layout/conversation_list_row.xml +++ b/src/main/res/layout/conversation_list_row.xml @@ -1,6 +1,5 @@ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" android:layout_height="wrap_content" android:descendantFocusability="blocksDescendants"> diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index c75396434..691fcd008 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -688,5 +688,6 @@ <string name="show_inactive_devices">Zeige inaktive Geräte</string> <string name="distrust_omemo_key">Gerät misstrauen</string> <string name="distrust_omemo_key_text">Bist du sicher, dass du die Verifizierung dieses Gerätes misstrauen möchtest? Dieses Gerät und Nachrichten von dem Gerät werden als unverifiziert markiert.</string> + <string name="transcoding_video_progress">Video wird komprimiert (%s%% fertiggestellt)</string> </resources> diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index ae18b1c4a..f34a17c9d 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -731,4 +731,5 @@ <string name="hide_inactive_devices">Hide inactive devices</string> <string name="reply">Reply</string> <string name="encrypting_message">Encrypting message</string> + <string name="transcoding_video_progress">Compressing video (%s%% completed)</string> </resources> |