headers)
+ throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
+ final String scheme = uri.getScheme();
+ if (ContentResolver.SCHEME_FILE.equals(scheme)) {
+ setDataSource(uri.getPath());
+ return;
+ } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)
+ && Settings.AUTHORITY.equals(uri.getAuthority())) {
+ // Redirect ringtones to go directly to underlying provider
+ uri = RingtoneManager.getActualDefaultRingtoneUri(context,
+ RingtoneManager.getDefaultType(uri));
+ if (uri == null) {
+ throw new FileNotFoundException("Failed to resolve default ringtone");
+ }
+ }
+
+ AssetFileDescriptor fd = null;
+ try {
+ ContentResolver resolver = context.getContentResolver();
+ fd = resolver.openAssetFileDescriptor(uri, "r");
+ if (fd == null) {
+ return;
+ }
+ // Note: using getDeclaredLength so that our behavior is the same
+ // as previous versions when the content provider is returning
+ // a full file.
+ if (fd.getDeclaredLength() < 0) {
+ setDataSource(fd.getFileDescriptor());
+ } else {
+ setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getDeclaredLength());
+ }
+ return;
+ } catch (SecurityException ignored) {
+ } catch (IOException ignored) {
+ } finally {
+ if (fd != null) {
+ fd.close();
+ }
+ }
+
+ Log.d(TAG, "Couldn't open file on client side, trying server side");
+
+ setDataSource(uri.toString(), headers);
+ }
+
+ /**
+ * Sets the data source (file-path or http/rtsp URL) to use.
+ *
+ * @param path
+ * the path of the file, or the http/rtsp URL of the stream you
+ * want to play
+ * @throws IllegalStateException
+ * if it is called in an invalid state
+ *
+ *
+ * When path
refers to a local file, the file may
+ * actually be opened by a process other than the calling
+ * application. This implies that the pathname should be an
+ * absolute path (as any other process runs with unspecified
+ * current working directory), and that the pathname should
+ * reference a world-readable file.
+ */
+ @Override
+ public void setDataSource(String path)
+ throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
+ mDataSource = path;
+ _setDataSource(path, null, null);
+ }
+
+ /**
+ * Sets the data source (file-path or http/rtsp URL) to use.
+ *
+ * @param path the path of the file, or the http/rtsp URL of the stream you want to play
+ * @param headers the headers associated with the http request for the stream you want to play
+ * @throws IllegalStateException if it is called in an invalid state
+ */
+ public void setDataSource(String path, Map headers)
+ throws IOException, IllegalArgumentException, SecurityException, IllegalStateException
+ {
+ if (headers != null && !headers.isEmpty()) {
+ StringBuilder sb = new StringBuilder();
+ for(Map.Entry entry: headers.entrySet()) {
+ sb.append(entry.getKey());
+ sb.append(":");
+ String value = entry.getValue();
+ if (!TextUtils.isEmpty(value))
+ sb.append(entry.getValue());
+ sb.append("\r\n");
+ setOption(OPT_CATEGORY_FORMAT, "headers", sb.toString());
+ }
+ }
+ setDataSource(path);
+ }
+
+ /**
+ * Sets the data source (FileDescriptor) to use. It is the caller's responsibility
+ * to close the file descriptor. It is safe to do so as soon as this call returns.
+ *
+ * @param fd the FileDescriptor for the file you want to play
+ * @throws IllegalStateException if it is called in an invalid state
+ */
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
+ @Override
+ public void setDataSource(FileDescriptor fd)
+ throws IOException, IllegalArgumentException, IllegalStateException {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR1) {
+ int native_fd = -1;
+ try {
+ Field f = fd.getClass().getDeclaredField("descriptor"); //NoSuchFieldException
+ f.setAccessible(true);
+ native_fd = f.getInt(fd); //IllegalAccessException
+ } catch (NoSuchFieldException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ _setDataSourceFd(native_fd);
+ } else {
+ ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(fd);
+ try {
+ _setDataSourceFd(pfd.getFd());
+ } finally {
+ pfd.close();
+ }
+ }
+ }
+
+ /**
+ * Sets the data source (FileDescriptor) to use. The FileDescriptor must be
+ * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility
+ * to close the file descriptor. It is safe to do so as soon as this call returns.
+ *
+ * @param fd the FileDescriptor for the file you want to play
+ * @param offset the offset into the file where the data to be played starts, in bytes
+ * @param length the length in bytes of the data to be played
+ * @throws IllegalStateException if it is called in an invalid state
+ */
+ private void setDataSource(FileDescriptor fd, long offset, long length)
+ throws IOException, IllegalArgumentException, IllegalStateException {
+ // FIXME: handle offset, length
+ setDataSource(fd);
+ }
+
+ public void setDataSource(IMediaDataSource mediaDataSource)
+ throws IllegalArgumentException, SecurityException, IllegalStateException {
+ _setDataSource(mediaDataSource);
+ }
+
+ private native void _setDataSource(String path, String[] keys, String[] values)
+ throws IOException, IllegalArgumentException, SecurityException, IllegalStateException;
+
+ private native void _setDataSourceFd(int fd)
+ throws IOException, IllegalArgumentException, SecurityException, IllegalStateException;
+
+ private native void _setDataSource(IMediaDataSource mediaDataSource)
+ throws IllegalArgumentException, SecurityException, IllegalStateException;
+
+ @Override
+ public String getDataSource() {
+ return mDataSource;
+ }
+
+ @Override
+ public void prepareAsync() throws IllegalStateException {
+ _prepareAsync();
+ }
+
+ public native void _prepareAsync() throws IllegalStateException;
+
+ @Override
+ public void start() throws IllegalStateException {
+ stayAwake(true);
+ _start();
+ }
+
+ private native void _start() throws IllegalStateException;
+
+ @Override
+ public void stop() throws IllegalStateException {
+ stayAwake(false);
+ _stop();
+ }
+
+ private native void _stop() throws IllegalStateException;
+
+ @Override
+ public void pause() throws IllegalStateException {
+ stayAwake(false);
+ _pause();
+ }
+
+ private native void _pause() throws IllegalStateException;
+
+ @SuppressLint("Wakelock")
+ @Override
+ public void setWakeMode(Context context, int mode) {
+ boolean washeld = false;
+ if (mWakeLock != null) {
+ if (mWakeLock.isHeld()) {
+ washeld = true;
+ mWakeLock.release();
+ }
+ mWakeLock = null;
+ }
+
+ PowerManager pm = (PowerManager) context
+ .getSystemService(Context.POWER_SERVICE);
+ mWakeLock = pm.newWakeLock(mode | PowerManager.ON_AFTER_RELEASE,
+ IjkMediaPlayer.class.getName());
+ mWakeLock.setReferenceCounted(false);
+ if (washeld) {
+ mWakeLock.acquire();
+ }
+ }
+
+ @Override
+ public void setScreenOnWhilePlaying(boolean screenOn) {
+ if (mScreenOnWhilePlaying != screenOn) {
+ if (screenOn && mSurfaceHolder == null) {
+ DebugLog.w(TAG,
+ "setScreenOnWhilePlaying(true) is ineffective without a SurfaceHolder");
+ }
+ mScreenOnWhilePlaying = screenOn;
+ updateSurfaceScreenOn();
+ }
+ }
+
+ @SuppressLint("Wakelock")
+ private void stayAwake(boolean awake) {
+ if (mWakeLock != null) {
+ if (awake && !mWakeLock.isHeld()) {
+ mWakeLock.acquire();
+ } else if (!awake && mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ }
+ mStayAwake = awake;
+ updateSurfaceScreenOn();
+ }
+
+ private void updateSurfaceScreenOn() {
+ if (mSurfaceHolder != null) {
+ mSurfaceHolder.setKeepScreenOn(mScreenOnWhilePlaying && mStayAwake);
+ }
+ }
+
+ @Override
+ public IjkTrackInfo[] getTrackInfo() {
+ Bundle bundle = getMediaMeta();
+ if (bundle == null)
+ return null;
+
+ IjkMediaMeta mediaMeta = IjkMediaMeta.parse(bundle);
+ if (mediaMeta == null || mediaMeta.mStreams == null)
+ return null;
+
+ ArrayList trackInfos = new ArrayList();
+ for (IjkMediaMeta.IjkStreamMeta streamMeta: mediaMeta.mStreams) {
+ IjkTrackInfo trackInfo = new IjkTrackInfo(streamMeta);
+ if (streamMeta.mType.equalsIgnoreCase(IjkMediaMeta.IJKM_VAL_TYPE__VIDEO)) {
+ trackInfo.setTrackType(ITrackInfo.MEDIA_TRACK_TYPE_VIDEO);
+ } else if (streamMeta.mType.equalsIgnoreCase(IjkMediaMeta.IJKM_VAL_TYPE__AUDIO)) {
+ trackInfo.setTrackType(ITrackInfo.MEDIA_TRACK_TYPE_AUDIO);
+ }
+ trackInfos.add(trackInfo);
+ }
+
+ return trackInfos.toArray(new IjkTrackInfo[trackInfos.size()]);
+ }
+
+ // TODO: @Override
+ public int getSelectedTrack(int trackType) {
+ switch (trackType) {
+ case ITrackInfo.MEDIA_TRACK_TYPE_VIDEO:
+ return (int)_getPropertyLong(FFP_PROP_INT64_SELECTED_VIDEO_STREAM, -1);
+ case ITrackInfo.MEDIA_TRACK_TYPE_AUDIO:
+ return (int)_getPropertyLong(FFP_PROP_INT64_SELECTED_AUDIO_STREAM, -1);
+ default:
+ return -1;
+ }
+ }
+
+ // experimental, should set DEFAULT_MIN_FRAMES and MAX_MIN_FRAMES to 25
+ // TODO: @Override
+ public void selectTrack(int track) {
+ _setStreamSelected(track, true);
+ }
+
+ // experimental, should set DEFAULT_MIN_FRAMES and MAX_MIN_FRAMES to 25
+ // TODO: @Override
+ public void deselectTrack(int track) {
+ _setStreamSelected(track, false);
+ }
+
+ private native void _setStreamSelected(int stream, boolean select);
+
+ @Override
+ public int getVideoWidth() {
+ return mVideoWidth;
+ }
+
+ @Override
+ public int getVideoHeight() {
+ return mVideoHeight;
+ }
+
+ @Override
+ public int getVideoSarNum() {
+ return mVideoSarNum;
+ }
+
+ @Override
+ public int getVideoSarDen() {
+ return mVideoSarDen;
+ }
+
+ @Override
+ public native boolean isPlaying();
+
+ @Override
+ public native void seekTo(long msec) throws IllegalStateException;
+
+ @Override
+ public native long getCurrentPosition();
+
+ @Override
+ public native long getDuration();
+
+ /**
+ * Releases resources associated with this IjkMediaPlayer object. It is
+ * considered good practice to call this method when you're done using the
+ * IjkMediaPlayer. In particular, whenever an Activity of an application is
+ * paused (its onPause() method is called), or stopped (its onStop() method
+ * is called), this method should be invoked to release the IjkMediaPlayer
+ * object, unless the application has a special need to keep the object
+ * around. In addition to unnecessary resources (such as memory and
+ * instances of codecs) being held, failure to call this method immediately
+ * if a IjkMediaPlayer object is no longer needed may also lead to
+ * continuous battery consumption for mobile devices, and playback failure
+ * for other applications if no multiple instances of the same codec are
+ * supported on a device. Even if multiple instances of the same codec are
+ * supported, some performance degradation may be expected when unnecessary
+ * multiple instances are used at the same time.
+ */
+ @Override
+ public void release() {
+ stayAwake(false);
+ updateSurfaceScreenOn();
+ resetListeners();
+ _release();
+ }
+
+ private native void _release();
+
+ @Override
+ public void reset() {
+ stayAwake(false);
+ _reset();
+ // make sure none of the listeners get called anymore
+ mEventHandler.removeCallbacksAndMessages(null);
+
+ mVideoWidth = 0;
+ mVideoHeight = 0;
+ }
+
+ private native void _reset();
+
+ /**
+ * Sets the player to be looping or non-looping.
+ *
+ * @param looping whether to loop or not
+ */
+ @Override
+ public void setLooping(boolean looping) {
+ int loopCount = looping ? 0 : 1;
+ setOption(OPT_CATEGORY_PLAYER, "loop", loopCount);
+ _setLoopCount(loopCount);
+ }
+
+ private native void _setLoopCount(int loopCount);
+
+ /**
+ * Checks whether the MediaPlayer is looping or non-looping.
+ *
+ * @return true if the MediaPlayer is currently looping, false otherwise
+ */
+ @Override
+ public boolean isLooping() {
+ int loopCount = _getLoopCount();
+ return loopCount != 1;
+ }
+
+ private native int _getLoopCount();
+
+ @TargetApi(Build.VERSION_CODES.M)
+ public void setSpeed(float speed) {
+ _setPropertyFloat(FFP_PROP_FLOAT_PLAYBACK_RATE, speed);
+ }
+
+ @TargetApi(Build.VERSION_CODES.M)
+ public float getSpeed(float speed) {
+ return _getPropertyFloat(FFP_PROP_FLOAT_PLAYBACK_RATE, .0f);
+ }
+
+ public int getVideoDecoder() {
+ return (int)_getPropertyLong(FFP_PROP_INT64_VIDEO_DECODER, FFP_PROPV_DECODER_UNKNOWN);
+ }
+
+ public float getVideoOutputFramesPerSecond() {
+ return _getPropertyFloat(PROP_FLOAT_VIDEO_OUTPUT_FRAMES_PER_SECOND, 0.0f);
+ }
+
+ public float getVideoDecodeFramesPerSecond() {
+ return _getPropertyFloat(PROP_FLOAT_VIDEO_DECODE_FRAMES_PER_SECOND, 0.0f);
+ }
+
+ public long getVideoCachedDuration() {
+ return _getPropertyLong(FFP_PROP_INT64_VIDEO_CACHED_DURATION, 0);
+ }
+
+ public long getAudioCachedDuration() {
+ return _getPropertyLong(FFP_PROP_INT64_AUDIO_CACHED_DURATION, 0);
+ }
+
+ public long getVideoCachedBytes() {
+ return _getPropertyLong(FFP_PROP_INT64_VIDEO_CACHED_BYTES, 0);
+ }
+
+ public long getAudioCachedBytes() {
+ return _getPropertyLong(FFP_PROP_INT64_AUDIO_CACHED_BYTES, 0);
+ }
+
+ public long getVideoCachedPackets() {
+ return _getPropertyLong(FFP_PROP_INT64_VIDEO_CACHED_PACKETS, 0);
+ }
+
+ public long getAudioCachedPackets() {
+ return _getPropertyLong(FFP_PROP_INT64_AUDIO_CACHED_PACKETS, 0);
+ }
+
+ private native float _getPropertyFloat(int property, float defaultValue);
+ private native void _setPropertyFloat(int property, float value);
+ private native long _getPropertyLong(int property, long defaultValue);
+ private native void _setPropertyLong(int property, long value);
+
+ @Override
+ public native void setVolume(float leftVolume, float rightVolume);
+
+ @Override
+ public native int getAudioSessionId();
+
+ @Override
+ public MediaInfo getMediaInfo() {
+ MediaInfo mediaInfo = new MediaInfo();
+ mediaInfo.mMediaPlayerName = "ijkplayer";
+
+ String videoCodecInfo = _getVideoCodecInfo();
+ if (!TextUtils.isEmpty(videoCodecInfo)) {
+ String nodes[] = videoCodecInfo.split(",");
+ if (nodes.length >= 2) {
+ mediaInfo.mVideoDecoder = nodes[0];
+ mediaInfo.mVideoDecoderImpl = nodes[1];
+ } else if (nodes.length >= 1) {
+ mediaInfo.mVideoDecoder = nodes[0];
+ mediaInfo.mVideoDecoderImpl = "";
+ }
+ }
+
+ String audioCodecInfo = _getAudioCodecInfo();
+ if (!TextUtils.isEmpty(audioCodecInfo)) {
+ String nodes[] = audioCodecInfo.split(",");
+ if (nodes.length >= 2) {
+ mediaInfo.mAudioDecoder = nodes[0];
+ mediaInfo.mAudioDecoderImpl = nodes[1];
+ } else if (nodes.length >= 1) {
+ mediaInfo.mAudioDecoder = nodes[0];
+ mediaInfo.mAudioDecoderImpl = "";
+ }
+ }
+
+ try {
+ mediaInfo.mMeta = IjkMediaMeta.parse(_getMediaMeta());
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ return mediaInfo;
+ }
+
+ @Override
+ public void setLogEnabled(boolean enable) {
+ // do nothing
+ }
+
+ @Override
+ public boolean isPlayable() {
+ return true;
+ }
+
+ private native String _getVideoCodecInfo();
+ private native String _getAudioCodecInfo();
+
+ public void setOption(int category, String name, String value)
+ {
+ _setOption(category, name, value);
+ }
+
+ public void setOption(int category, String name, long value)
+ {
+ _setOption(category, name, value);
+ }
+
+ private native void _setOption(int category, String name, String value);
+ private native void _setOption(int category, String name, long value);
+
+ public Bundle getMediaMeta() {
+ return _getMediaMeta();
+ }
+ private native Bundle _getMediaMeta();
+
+ public static String getColorFormatName(int mediaCodecColorFormat) {
+ return _getColorFormatName(mediaCodecColorFormat);
+ }
+
+ private static native String _getColorFormatName(int mediaCodecColorFormat);
+
+ @Override
+ public void setAudioStreamType(int streamtype) {
+ // do nothing
+ }
+
+ @Override
+ public void setKeepInBackground(boolean keepInBackground) {
+ // do nothing
+ }
+
+ private static native void native_init();
+
+ private native void native_setup(Object IjkMediaPlayer_this);
+
+ private native void native_finalize();
+
+ private native void native_message_loop(Object IjkMediaPlayer_this);
+
+ protected void finalize() throws Throwable {
+ super.finalize();
+ native_finalize();
+ }
+
+ private static class EventHandler extends Handler {
+ private final WeakReference mWeakPlayer;
+
+ public EventHandler(IjkMediaPlayer mp, Looper looper) {
+ super(looper);
+ mWeakPlayer = new WeakReference(mp);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ IjkMediaPlayer player = mWeakPlayer.get();
+ if (player == null || player.mNativeMediaPlayer == 0) {
+ DebugLog.w(TAG,
+ "IjkMediaPlayer went away with unhandled events");
+ return;
+ }
+
+ switch (msg.what) {
+ case MEDIA_PREPARED:
+ player.notifyOnPrepared();
+ return;
+
+ case MEDIA_PLAYBACK_COMPLETE:
+ player.stayAwake(false);
+ player.notifyOnCompletion();
+ return;
+
+ case MEDIA_BUFFERING_UPDATE:
+ long bufferPosition = msg.arg1;
+ if (bufferPosition < 0) {
+ bufferPosition = 0;
+ }
+
+ long percent = 0;
+ long duration = player.getDuration();
+ if (duration > 0) {
+ percent = bufferPosition * 100 / duration;
+ }
+ if (percent >= 100) {
+ percent = 100;
+ }
+
+ // DebugLog.efmt(TAG, "Buffer (%d%%) %d/%d", percent, bufferPosition, duration);
+ player.notifyOnBufferingUpdate((int)percent);
+ return;
+
+ case MEDIA_SEEK_COMPLETE:
+ player.notifyOnSeekComplete();
+ return;
+
+ case MEDIA_SET_VIDEO_SIZE:
+ player.mVideoWidth = msg.arg1;
+ player.mVideoHeight = msg.arg2;
+ player.notifyOnVideoSizeChanged(player.mVideoWidth, player.mVideoHeight,
+ player.mVideoSarNum, player.mVideoSarDen);
+ return;
+
+ case MEDIA_ERROR:
+ DebugLog.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
+ if (!player.notifyOnError(msg.arg1, msg.arg2)) {
+ player.notifyOnCompletion();
+ }
+ player.stayAwake(false);
+ return;
+
+ case MEDIA_INFO:
+ switch (msg.arg1) {
+ case MEDIA_INFO_VIDEO_RENDERING_START:
+ DebugLog.i(TAG, "Info: MEDIA_INFO_VIDEO_RENDERING_START\n");
+ break;
+ }
+ player.notifyOnInfo(msg.arg1, msg.arg2);
+ // No real default action so far.
+ return;
+ case MEDIA_TIMED_TEXT:
+ // do nothing
+ break;
+
+ case MEDIA_NOP: // interface test message - ignore
+ break;
+
+ case MEDIA_SET_VIDEO_SAR:
+ player.mVideoSarNum = msg.arg1;
+ player.mVideoSarDen = msg.arg2;
+ player.notifyOnVideoSizeChanged(player.mVideoWidth, player.mVideoHeight,
+ player.mVideoSarNum, player.mVideoSarDen);
+ break;
+
+ default:
+ DebugLog.e(TAG, "Unknown message type " + msg.what);
+ }
+ }
+ }
+
+ /*
+ * Called from native code when an interesting event happens. This method
+ * just uses the EventHandler system to post the event back to the main app
+ * thread. We use a weak reference to the original IjkMediaPlayer object so
+ * that the native code is safe from the object disappearing from underneath
+ * it. (This is the cookie passed to native_setup().)
+ */
+ @CalledByNative
+ private static void postEventFromNative(Object weakThiz, int what,
+ int arg1, int arg2, Object obj) {
+ if (weakThiz == null)
+ return;
+
+ @SuppressWarnings("rawtypes")
+ IjkMediaPlayer mp = (IjkMediaPlayer) ((WeakReference) weakThiz).get();
+ if (mp == null) {
+ return;
+ }
+
+ if (what == MEDIA_INFO && arg1 == MEDIA_INFO_STARTED_AS_NEXT) {
+ // this acquires the wakelock if needed, and sets the client side
+ // state
+ mp.start();
+ }
+ if (mp.mEventHandler != null) {
+ Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);
+ mp.mEventHandler.sendMessage(m);
+ }
+ }
+
+ /*
+ * ControlMessage
+ */
+
+ private OnControlMessageListener mOnControlMessageListener;
+ public void setOnControlMessageListener(OnControlMessageListener listener) {
+ mOnControlMessageListener = listener;
+ }
+
+ public interface OnControlMessageListener {
+ String onControlResolveSegmentUrl(int segment);
+ }
+
+ /*
+ * NativeInvoke
+ */
+
+ private OnNativeInvokeListener mOnNativeInvokeListener;
+ public void setOnNativeInvokeListener(OnNativeInvokeListener listener) {
+ mOnNativeInvokeListener = listener;
+ }
+
+ public interface OnNativeInvokeListener {
+ int ON_CONCAT_RESOLVE_SEGMENT = 0x10000;
+ int ON_TCP_OPEN = 0x10001;
+ int ON_HTTP_OPEN = 0x10002;
+ // int ON_HTTP_RETRY = 0x10003;
+ int ON_LIVE_RETRY = 0x10004;
+
+ String ARG_URL = "url";
+ String ARG_SEGMENT_INDEX = "segment_index";
+ String ARG_RETRY_COUNTER = "retry_counter";
+
+ /*
+ * @return true if invoke is handled
+ * @throws Exception on any error
+ */
+ boolean onNativeInvoke(int what, Bundle args);
+ }
+
+ @CalledByNative
+ private static boolean onNativeInvoke(Object weakThiz, int what, Bundle args) {
+ DebugLog.ifmt(TAG, "onNativeInvoke %d", what);
+ if (weakThiz == null || !(weakThiz instanceof WeakReference>))
+ throw new IllegalStateException(".onNativeInvoke()");
+
+ @SuppressWarnings("unchecked")
+ WeakReference weakPlayer = (WeakReference) weakThiz;
+ IjkMediaPlayer player = weakPlayer.get();
+ if (player == null)
+ throw new IllegalStateException(".onNativeInvoke()");
+
+ OnNativeInvokeListener listener = player.mOnNativeInvokeListener;
+ if (listener != null && listener.onNativeInvoke(what, args))
+ return true;
+
+ switch (what) {
+ case OnNativeInvokeListener.ON_CONCAT_RESOLVE_SEGMENT: {
+ OnControlMessageListener onControlMessageListener = player.mOnControlMessageListener;
+ if (onControlMessageListener == null)
+ return false;
+
+ int segmentIndex = args.getInt(OnNativeInvokeListener.ARG_SEGMENT_INDEX, -1);
+ if (segmentIndex < 0)
+ throw new InvalidParameterException("onNativeInvoke(invalid segment index)");
+
+ String newUrl = onControlMessageListener.onControlResolveSegmentUrl(segmentIndex);
+ if (newUrl == null)
+ throw new RuntimeException(new IOException("onNativeInvoke() = "));
+
+ args.putString(OnNativeInvokeListener.ARG_URL, newUrl);
+ return true;
+ }
+ default:
+ return false;
+ }
+ }
+
+ /*
+ * MediaCodec select
+ */
+
+ public interface OnMediaCodecSelectListener {
+ String onMediaCodecSelect(IMediaPlayer mp, String mimeType, int profile, int level);
+ }
+ private OnMediaCodecSelectListener mOnMediaCodecSelectListener;
+ public void setOnMediaCodecSelectListener(OnMediaCodecSelectListener listener) {
+ mOnMediaCodecSelectListener = listener;
+ }
+
+ public void resetListeners() {
+ super.resetListeners();
+ mOnMediaCodecSelectListener = null;
+ }
+
+ @CalledByNative
+ private static String onSelectCodec(Object weakThiz, String mimeType, int profile, int level) {
+ if (weakThiz == null || !(weakThiz instanceof WeakReference>))
+ return null;
+
+ @SuppressWarnings("unchecked")
+ WeakReference weakPlayer = (WeakReference) weakThiz;
+ IjkMediaPlayer player = weakPlayer.get();
+ if (player == null)
+ return null;
+
+ OnMediaCodecSelectListener listener = player.mOnMediaCodecSelectListener;
+ if (listener == null)
+ listener = DefaultMediaCodecSelector.sInstance;
+
+ return listener.onMediaCodecSelect(player, mimeType, profile, level);
+ }
+
+ public static class DefaultMediaCodecSelector implements OnMediaCodecSelectListener {
+ public static final DefaultMediaCodecSelector sInstance = new DefaultMediaCodecSelector();
+
+ @SuppressWarnings("deprecation")
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ public String onMediaCodecSelect(IMediaPlayer mp, String mimeType, int profile, int level) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
+ return null;
+
+ if (TextUtils.isEmpty(mimeType))
+ return null;
+
+ Log.i(TAG, String.format(Locale.US, "onSelectCodec: mime=%s, profile=%d, level=%d", mimeType, profile, level));
+ ArrayList candidateCodecList = new ArrayList();
+ int numCodecs = MediaCodecList.getCodecCount();
+ for (int i = 0; i < numCodecs; i++) {
+ MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
+ Log.d(TAG, String.format(Locale.US, " found codec: %s", codecInfo.getName()));
+ if (codecInfo.isEncoder())
+ continue;
+
+ String[] types = codecInfo.getSupportedTypes();
+ if (types == null)
+ continue;
+
+ for(String type: types) {
+ if (TextUtils.isEmpty(type))
+ continue;
+
+ Log.d(TAG, String.format(Locale.US, " mime: %s", type));
+ if (!type.equalsIgnoreCase(mimeType))
+ continue;
+
+ IjkMediaCodecInfo candidate = IjkMediaCodecInfo.setupCandidate(codecInfo, mimeType);
+ if (candidate == null)
+ continue;
+
+ candidateCodecList.add(candidate);
+ Log.i(TAG, String.format(Locale.US, "candidate codec: %s rank=%d", codecInfo.getName(), candidate.mRank));
+ candidate.dumpProfileLevels(mimeType);
+ }
+ }
+
+ if (candidateCodecList.isEmpty()) {
+ return null;
+ }
+
+ IjkMediaCodecInfo bestCodec = candidateCodecList.get(0);
+
+ for (IjkMediaCodecInfo codec : candidateCodecList) {
+ if (codec.mRank > bestCodec.mRank) {
+ bestCodec = codec;
+ }
+ }
+
+ if (bestCodec.mRank < IjkMediaCodecInfo.RANK_LAST_CHANCE) {
+ Log.w(TAG, String.format(Locale.US, "unaccetable codec: %s", bestCodec.mCodecInfo.getName()));
+ return null;
+ }
+
+ Log.i(TAG, String.format(Locale.US, "selected codec: %s rank=%d", bestCodec.mCodecInfo.getName(), bestCodec.mRank));
+ return bestCodec.mCodecInfo.getName();
+ }
+ }
+
+ public static native void native_profileBegin(String libName);
+ public static native void native_profileEnd();
+ public static native void native_setLogLevel(int level);
+}
diff --git a/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/MediaInfo.java b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/MediaInfo.java
new file mode 100644
index 0000000..6cd5004
--- /dev/null
+++ b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/MediaInfo.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2013-2014 Zhang Rui
+ *
+ * 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 tv.danmaku.ijk.media.player;
+
+public class MediaInfo {
+ public String mMediaPlayerName;
+
+ public String mVideoDecoder;
+ public String mVideoDecoderImpl;
+
+ public String mAudioDecoder;
+ public String mAudioDecoderImpl;
+
+ public IjkMediaMeta mMeta;
+}
diff --git a/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/MediaPlayerProxy.java b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/MediaPlayerProxy.java
new file mode 100644
index 0000000..3dfbb1a
--- /dev/null
+++ b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/MediaPlayerProxy.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2015 Zhang Rui
+ *
+ * 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 tv.danmaku.ijk.media.player;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Build;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.util.Map;
+
+import tv.danmaku.ijk.media.player.misc.IMediaDataSource;
+import tv.danmaku.ijk.media.player.misc.ITrackInfo;
+
+public class MediaPlayerProxy implements IMediaPlayer {
+ protected final IMediaPlayer mBackEndMediaPlayer;
+
+ public MediaPlayerProxy(IMediaPlayer backEndMediaPlayer) {
+ mBackEndMediaPlayer = backEndMediaPlayer;
+ }
+
+ public IMediaPlayer getInternalMediaPlayer() {
+ return mBackEndMediaPlayer;
+ }
+
+ @Override
+ public void setDisplay(SurfaceHolder sh) {
+ mBackEndMediaPlayer.setDisplay(sh);
+ }
+
+ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ @Override
+ public void setSurface(Surface surface) {
+ mBackEndMediaPlayer.setSurface(surface);
+ }
+
+ @Override
+ public void setDataSource(Context context, Uri uri)
+ throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
+ mBackEndMediaPlayer.setDataSource(context, uri);
+ }
+
+ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ @Override
+ public void setDataSource(Context context, Uri uri, Map headers)
+ throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
+ mBackEndMediaPlayer.setDataSource(context, uri, headers);
+ }
+
+ @Override
+ public void setDataSource(FileDescriptor fd)
+ throws IOException, IllegalArgumentException, IllegalStateException {
+ mBackEndMediaPlayer.setDataSource(fd);
+ }
+
+ @Override
+ public void setDataSource(String path) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
+ mBackEndMediaPlayer.setDataSource(path);
+ }
+
+ @Override
+ public void setDataSource(IMediaDataSource mediaDataSource) {
+ mBackEndMediaPlayer.setDataSource(mediaDataSource);
+ }
+
+ @Override
+ public String getDataSource() {
+ return mBackEndMediaPlayer.getDataSource();
+ }
+
+ @Override
+ public void prepareAsync() throws IllegalStateException {
+ mBackEndMediaPlayer.prepareAsync();
+ }
+
+ @Override
+ public void start() throws IllegalStateException {
+ mBackEndMediaPlayer.start();
+ }
+
+ @Override
+ public void stop() throws IllegalStateException {
+ mBackEndMediaPlayer.stop();
+ }
+
+ @Override
+ public void pause() throws IllegalStateException {
+ mBackEndMediaPlayer.pause();
+ }
+
+ @Override
+ public void setScreenOnWhilePlaying(boolean screenOn) {
+ mBackEndMediaPlayer.setScreenOnWhilePlaying(screenOn);
+ }
+
+ @Override
+ public int getVideoWidth() {
+ return mBackEndMediaPlayer.getVideoWidth();
+ }
+
+ @Override
+ public int getVideoHeight() {
+ return mBackEndMediaPlayer.getVideoHeight();
+ }
+
+ @Override
+ public boolean isPlaying() {
+ return mBackEndMediaPlayer.isPlaying();
+ }
+
+ @Override
+ public void seekTo(long msec) throws IllegalStateException {
+ mBackEndMediaPlayer.seekTo(msec);
+ }
+
+ @Override
+ public long getCurrentPosition() {
+ return mBackEndMediaPlayer.getCurrentPosition();
+ }
+
+ @Override
+ public long getDuration() {
+ return mBackEndMediaPlayer.getDuration();
+ }
+
+ @Override
+ public void release() {
+ mBackEndMediaPlayer.release();
+ }
+
+ @Override
+ public void reset() {
+ mBackEndMediaPlayer.reset();
+ }
+
+ @Override
+ public void setVolume(float leftVolume, float rightVolume) {
+ mBackEndMediaPlayer.setVolume(leftVolume, rightVolume);
+ }
+
+ @Override
+ public int getAudioSessionId() {
+ return mBackEndMediaPlayer.getAudioSessionId();
+ }
+
+ @Override
+ public MediaInfo getMediaInfo() {
+ return mBackEndMediaPlayer.getMediaInfo();
+ }
+
+ @Override
+ public void setLogEnabled(boolean enable) {
+
+ }
+
+ @Override
+ public boolean isPlayable() {
+ return false;
+ }
+
+ @Override
+ public void setOnPreparedListener(OnPreparedListener listener) {
+ if (listener != null) {
+ final OnPreparedListener finalListener = listener;
+ mBackEndMediaPlayer.setOnPreparedListener(new OnPreparedListener() {
+ @Override
+ public void onPrepared(IMediaPlayer mp) {
+ finalListener.onPrepared(MediaPlayerProxy.this);
+ }
+ });
+ } else {
+ mBackEndMediaPlayer.setOnPreparedListener(null);
+ }
+ }
+
+ @Override
+ public void setOnCompletionListener(OnCompletionListener listener) {
+ if (listener != null) {
+ final OnCompletionListener finalListener = listener;
+ mBackEndMediaPlayer.setOnCompletionListener(new OnCompletionListener() {
+ @Override
+ public void onCompletion(IMediaPlayer mp) {
+ finalListener.onCompletion(MediaPlayerProxy.this);
+ }
+ });
+ } else {
+ mBackEndMediaPlayer.setOnCompletionListener(null);
+ }
+ }
+
+ @Override
+ public void setOnBufferingUpdateListener(OnBufferingUpdateListener listener) {
+ if (listener != null) {
+ final OnBufferingUpdateListener finalListener = listener;
+ mBackEndMediaPlayer.setOnBufferingUpdateListener(new OnBufferingUpdateListener() {
+ @Override
+ public void onBufferingUpdate(IMediaPlayer mp, int percent) {
+ finalListener.onBufferingUpdate(MediaPlayerProxy.this, percent);
+ }
+ });
+ } else {
+ mBackEndMediaPlayer.setOnBufferingUpdateListener(null);
+ }
+ }
+
+ @Override
+ public void setOnSeekCompleteListener(OnSeekCompleteListener listener) {
+ if (listener != null) {
+ final OnSeekCompleteListener finalListener = listener;
+ mBackEndMediaPlayer.setOnSeekCompleteListener(new OnSeekCompleteListener() {
+ @Override
+ public void onSeekComplete(IMediaPlayer mp) {
+ finalListener.onSeekComplete(MediaPlayerProxy.this);
+ }
+ });
+ } else {
+ mBackEndMediaPlayer.setOnSeekCompleteListener(null);
+ }
+ }
+
+ @Override
+ public void setOnVideoSizeChangedListener(OnVideoSizeChangedListener listener) {
+ if (listener != null) {
+ final OnVideoSizeChangedListener finalListener = listener;
+ mBackEndMediaPlayer.setOnVideoSizeChangedListener(new OnVideoSizeChangedListener() {
+ @Override
+ public void onVideoSizeChanged(IMediaPlayer mp, int width, int height, int sar_num, int sar_den) {
+ finalListener.onVideoSizeChanged(MediaPlayerProxy.this, width, height, sar_num, sar_den);
+ }
+ });
+ } else {
+ mBackEndMediaPlayer.setOnVideoSizeChangedListener(null);
+ }
+ }
+
+ @Override
+ public void setOnErrorListener(OnErrorListener listener) {
+ if (listener != null) {
+ final OnErrorListener finalListener = listener;
+ mBackEndMediaPlayer.setOnErrorListener(new OnErrorListener() {
+ @Override
+ public boolean onError(IMediaPlayer mp, int what, int extra) {
+ return finalListener.onError(MediaPlayerProxy.this, what, extra);
+ }
+ });
+ } else {
+ mBackEndMediaPlayer.setOnErrorListener(null);
+ }
+ }
+
+ @Override
+ public void setOnInfoListener(OnInfoListener listener) {
+ if (listener != null) {
+ final OnInfoListener finalListener = listener;
+ mBackEndMediaPlayer.setOnInfoListener(new OnInfoListener() {
+ @Override
+ public boolean onInfo(IMediaPlayer mp, int what, int extra) {
+ return finalListener.onInfo(MediaPlayerProxy.this, what, extra);
+ }
+ });
+ } else {
+ mBackEndMediaPlayer.setOnInfoListener(null);
+ }
+ }
+
+ @Override
+ public void setAudioStreamType(int streamtype) {
+ mBackEndMediaPlayer.setAudioStreamType(streamtype);
+ }
+
+ @Override
+ public void setKeepInBackground(boolean keepInBackground) {
+ mBackEndMediaPlayer.setKeepInBackground(keepInBackground);
+ }
+
+ @Override
+ public int getVideoSarNum() {
+ return mBackEndMediaPlayer.getVideoSarNum();
+ }
+
+ @Override
+ public int getVideoSarDen() {
+ return mBackEndMediaPlayer.getVideoSarDen();
+ }
+
+ @Override
+ public void setWakeMode(Context context, int mode) {
+ mBackEndMediaPlayer.setWakeMode(context, mode);
+ }
+
+ @Override
+ public ITrackInfo[] getTrackInfo() {
+ return mBackEndMediaPlayer.getTrackInfo();
+ }
+
+ @Override
+ public void setLooping(boolean looping) {
+ mBackEndMediaPlayer.setLooping(looping);
+ }
+
+ @Override
+ public boolean isLooping() {
+ return mBackEndMediaPlayer.isLooping();
+ }
+}
diff --git a/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/TextureMediaPlayer.java b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/TextureMediaPlayer.java
new file mode 100644
index 0000000..22a269e
--- /dev/null
+++ b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/TextureMediaPlayer.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2015 Zhang Rui
+ *
+ * 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 tv.danmaku.ijk.media.player;
+
+import android.annotation.TargetApi;
+import android.graphics.SurfaceTexture;
+import android.os.Build;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+public class TextureMediaPlayer extends MediaPlayerProxy implements IMediaPlayer, ISurfaceTextureHolder {
+ private SurfaceTexture mSurfaceTexture;
+ private ISurfaceTextureHost mSurfaceTextureHost;
+
+ public TextureMediaPlayer(IMediaPlayer backEndMediaPlayer) {
+ super(backEndMediaPlayer);
+ }
+
+ public void releaseSurfaceTexture() {
+ if (mSurfaceTexture != null) {
+ if (mSurfaceTextureHost != null) {
+ mSurfaceTextureHost.releaseSurfaceTexture(mSurfaceTexture);
+ } else {
+ mSurfaceTexture.release();
+ }
+ mSurfaceTexture = null;
+ }
+ }
+
+ //--------------------
+ // IMediaPlayer
+ //--------------------
+ @Override
+ public void reset() {
+ super.reset();
+ releaseSurfaceTexture();
+ }
+
+ @Override
+ public void release() {
+ super.release();
+ releaseSurfaceTexture();
+ }
+
+ @Override
+ public void setDisplay(SurfaceHolder sh) {
+ if (mSurfaceTexture == null)
+ super.setDisplay(sh);
+ }
+
+ @Override
+ public void setSurface(Surface surface) {
+ if (mSurfaceTexture == null)
+ super.setSurface(surface);
+ }
+
+ //--------------------
+ // ISurfaceTextureHolder
+ //--------------------
+
+ @Override
+ public void setSurfaceTexture(SurfaceTexture surfaceTexture) {
+ if (mSurfaceTexture == surfaceTexture)
+ return;
+
+ releaseSurfaceTexture();
+ mSurfaceTexture = surfaceTexture;
+ if (surfaceTexture == null) {
+ super.setSurface(null);
+ } else {
+ super.setSurface(new Surface(surfaceTexture));
+ }
+ }
+
+ @Override
+ public SurfaceTexture getSurfaceTexture() {
+ return mSurfaceTexture;
+ }
+
+ @Override
+ public void setSurfaceTextureHost(ISurfaceTextureHost surfaceTextureHost) {
+ mSurfaceTextureHost = surfaceTextureHost;
+ }
+}
diff --git a/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/annotations/AccessedByNative.java b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/annotations/AccessedByNative.java
new file mode 100644
index 0000000..c613660
--- /dev/null
+++ b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/annotations/AccessedByNative.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2013-2014 Zhang Rui
+ *
+ * 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 tv.danmaku.ijk.media.player.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * is used by the JNI generator to create the necessary JNI
+ * bindings and expose this method to native code.
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.CLASS)
+public @interface AccessedByNative {
+}
\ No newline at end of file
diff --git a/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/annotations/CalledByNative.java b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/annotations/CalledByNative.java
new file mode 100644
index 0000000..2a2caf3
--- /dev/null
+++ b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/annotations/CalledByNative.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2013-2014 Zhang Rui
+ *
+ * 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 tv.danmaku.ijk.media.player.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * is used by the JNI generator to create the necessary JNI
+ * bindings and expose this method to native code.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.CLASS)
+public @interface CalledByNative {
+ /*
+ * If present, tells which inner class the method belongs to.
+ */
+ String value() default "";
+}
\ No newline at end of file
diff --git a/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/exceptions/IjkMediaException.java b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/exceptions/IjkMediaException.java
new file mode 100644
index 0000000..dbd1add
--- /dev/null
+++ b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/exceptions/IjkMediaException.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2013-2014 Zhang Rui
+ *
+ * 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 tv.danmaku.ijk.media.player.exceptions;
+
+public class IjkMediaException extends Exception {
+ private static final long serialVersionUID = 7234796519009099506L;
+}
diff --git a/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/ffmpeg/FFmpegApi.java b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/ffmpeg/FFmpegApi.java
new file mode 100644
index 0000000..e13ba1b
--- /dev/null
+++ b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/ffmpeg/FFmpegApi.java
@@ -0,0 +1,5 @@
+package tv.danmaku.ijk.media.player.ffmpeg;
+
+public class FFmpegApi {
+ public static native String av_base64_encode(byte in[]);
+}
diff --git a/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/AndroidMediaFormat.java b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/AndroidMediaFormat.java
new file mode 100644
index 0000000..9338a24
--- /dev/null
+++ b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/AndroidMediaFormat.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 Zhang Rui
+ *
+ * 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 tv.danmaku.ijk.media.player.misc;
+
+import android.annotation.TargetApi;
+import android.media.MediaFormat;
+import android.os.Build;
+
+public class AndroidMediaFormat implements IMediaFormat {
+ private final MediaFormat mMediaFormat;
+
+ public AndroidMediaFormat(MediaFormat mediaFormat) {
+ mMediaFormat = mediaFormat;
+ }
+
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ @Override
+ public int getInteger(String name) {
+ if (mMediaFormat == null)
+ return 0;
+
+ return mMediaFormat.getInteger(name);
+ }
+
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ @Override
+ public String getString(String name) {
+ if (mMediaFormat == null)
+ return null;
+
+ return mMediaFormat.getString(name);
+ }
+
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ @Override
+ public String toString() {
+ StringBuilder out = new StringBuilder(128);
+ out.append(getClass().getName());
+ out.append('{');
+ if (mMediaFormat != null) {
+ out.append(mMediaFormat.toString());
+ } else {
+ out.append("null");
+ }
+ out.append('}');
+ return out.toString();
+ }
+}
diff --git a/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/AndroidTrackInfo.java b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/AndroidTrackInfo.java
new file mode 100644
index 0000000..33e7556
--- /dev/null
+++ b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/AndroidTrackInfo.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 Zhang Rui
+ *
+ * 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 tv.danmaku.ijk.media.player.misc;
+
+import android.annotation.TargetApi;
+import android.media.MediaFormat;
+import android.media.MediaPlayer;
+import android.os.Build;
+
+public class AndroidTrackInfo implements ITrackInfo {
+ private final MediaPlayer.TrackInfo mTrackInfo;
+
+ public static AndroidTrackInfo[] fromMediaPlayer(MediaPlayer mp) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
+ return fromTrackInfo(mp.getTrackInfo());
+
+ return null;
+ }
+
+ private static AndroidTrackInfo[] fromTrackInfo(MediaPlayer.TrackInfo[] trackInfos) {
+ if (trackInfos == null)
+ return null;
+
+ AndroidTrackInfo androidTrackInfo[] = new AndroidTrackInfo[trackInfos.length];
+ for (int i = 0; i < trackInfos.length; ++i) {
+ androidTrackInfo[i] = new AndroidTrackInfo(trackInfos[i]);
+ }
+
+ return androidTrackInfo;
+ }
+
+ private AndroidTrackInfo(MediaPlayer.TrackInfo trackInfo) {
+ mTrackInfo = trackInfo;
+ }
+
+ @TargetApi(Build.VERSION_CODES.KITKAT)
+ @Override
+ public IMediaFormat getFormat() {
+ if (mTrackInfo == null)
+ return null;
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
+ return null;
+
+ MediaFormat mediaFormat = mTrackInfo.getFormat();
+ if (mediaFormat == null)
+ return null;
+
+ return new AndroidMediaFormat(mediaFormat);
+ }
+
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ @Override
+ public String getLanguage() {
+ if (mTrackInfo == null)
+ return "und";
+
+ return mTrackInfo.getLanguage();
+ }
+
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ @Override
+ public int getTrackType() {
+ if (mTrackInfo == null)
+ return MEDIA_TRACK_TYPE_UNKNOWN;
+
+ return mTrackInfo.getTrackType();
+ }
+
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ @Override
+ public String toString() {
+ StringBuilder out = new StringBuilder(128);
+ out.append(getClass().getSimpleName());
+ out.append('{');
+ if (mTrackInfo != null) {
+ out.append(mTrackInfo.toString());
+ } else {
+ out.append("null");
+ }
+ out.append('}');
+ return out.toString();
+ }
+
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ @Override
+ public String getInfoInline() {
+ if (mTrackInfo != null) {
+ return mTrackInfo.toString();
+ } else {
+ return "null";
+ }
+ }
+}
diff --git a/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/IMediaDataSource.java b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/IMediaDataSource.java
new file mode 100644
index 0000000..a298aa9
--- /dev/null
+++ b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/IMediaDataSource.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 Zhang Rui
+ *
+ * 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 tv.danmaku.ijk.media.player.misc;
+
+import java.io.IOException;
+
+@SuppressWarnings("RedundantThrows")
+public interface IMediaDataSource {
+ int readAt(long position, byte[] buffer, int offset, int size) throws IOException;
+
+ long getSize() throws IOException;
+
+ void close() throws IOException;
+}
diff --git a/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/IMediaFormat.java b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/IMediaFormat.java
new file mode 100644
index 0000000..b1cd4db
--- /dev/null
+++ b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/IMediaFormat.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 Zhang Rui
+ *
+ * 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 tv.danmaku.ijk.media.player.misc;
+
+public interface IMediaFormat {
+ // Common keys
+ String KEY_MIME = "mime";
+
+ // Video Keys
+ String KEY_WIDTH = "width";
+ String KEY_HEIGHT = "height";
+
+ String getString(String name);
+
+ int getInteger(String name);
+}
diff --git a/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/ITrackInfo.java b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/ITrackInfo.java
new file mode 100644
index 0000000..a9c5101
--- /dev/null
+++ b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/ITrackInfo.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 Zhang Rui
+ *
+ * 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 tv.danmaku.ijk.media.player.misc;
+
+public interface ITrackInfo {
+ int MEDIA_TRACK_TYPE_AUDIO = 2;
+ int MEDIA_TRACK_TYPE_METADATA = 5;
+ int MEDIA_TRACK_TYPE_SUBTITLE = 4;
+ int MEDIA_TRACK_TYPE_TIMEDTEXT = 3;
+ int MEDIA_TRACK_TYPE_UNKNOWN = 0;
+ int MEDIA_TRACK_TYPE_VIDEO = 1;
+
+ IMediaFormat getFormat();
+
+ String getLanguage();
+
+ int getTrackType();
+
+ String getInfoInline();
+}
diff --git a/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/IjkMediaFormat.java b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/IjkMediaFormat.java
new file mode 100644
index 0000000..f6ca505
--- /dev/null
+++ b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/IjkMediaFormat.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2015 Zhang Rui
+ *
+ * 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 tv.danmaku.ijk.media.player.misc;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.text.TextUtils;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import tv.danmaku.ijk.media.player.IjkMediaMeta;
+
+public class IjkMediaFormat implements IMediaFormat {
+ // Common
+ public static final String KEY_IJK_CODEC_LONG_NAME_UI = "ijk-codec-long-name-ui";
+ public static final String KEY_IJK_BIT_RATE_UI = "ijk-bit-rate-ui";
+
+ // Video
+ public static final String KEY_IJK_CODEC_PROFILE_LEVEL_UI = "ijk-profile-level-ui";
+ public static final String KEY_IJK_CODEC_PIXEL_FORMAT_UI = "ijk-pixel-format-ui";
+ public static final String KEY_IJK_RESOLUTION_UI = "ijk-resolution-ui";
+ public static final String KEY_IJK_FRAME_RATE_UI = "ijk-frame-rate-ui";
+
+ // Audio
+ public static final String KEY_IJK_SAMPLE_RATE_UI = "ijk-sample-rate-ui";
+ public static final String KEY_IJK_CHANNEL_UI = "ijk-channel-ui";
+
+ // Codec
+ public static final String CODEC_NAME_H264 = "h264";
+
+ public final IjkMediaMeta.IjkStreamMeta mMediaFormat;
+
+ public IjkMediaFormat(IjkMediaMeta.IjkStreamMeta streamMeta) {
+ mMediaFormat = streamMeta;
+ }
+
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ @Override
+ public int getInteger(String name) {
+ if (mMediaFormat == null)
+ return 0;
+
+ return mMediaFormat.getInt(name);
+ }
+
+ @Override
+ public String getString(String name) {
+ if (mMediaFormat == null)
+ return null;
+
+ if (sFormatterMap.containsKey(name)) {
+ Formatter formatter = sFormatterMap.get(name);
+ return formatter.format(this);
+ }
+
+ return mMediaFormat.getString(name);
+ }
+
+ //-------------------------
+ // Formatter
+ //-------------------------
+
+ private static abstract class Formatter {
+ public String format(IjkMediaFormat mediaFormat) {
+ String value = doFormat(mediaFormat);
+ if (TextUtils.isEmpty(value))
+ return getDefaultString();
+ return value;
+ }
+
+ protected abstract String doFormat(IjkMediaFormat mediaFormat);
+
+ @SuppressWarnings("SameReturnValue")
+ protected String getDefaultString() {
+ return "N/A";
+ }
+ }
+
+ private static final Map sFormatterMap = new HashMap();
+
+ {
+ sFormatterMap.put(KEY_IJK_CODEC_LONG_NAME_UI, new Formatter() {
+ @Override
+ public String doFormat(IjkMediaFormat mediaFormat) {
+ return mMediaFormat.getString(IjkMediaMeta.IJKM_KEY_CODEC_LONG_NAME);
+ }
+ });
+ sFormatterMap.put(KEY_IJK_BIT_RATE_UI, new Formatter() {
+ @Override
+ protected String doFormat(IjkMediaFormat mediaFormat) {
+ int bitRate = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_BITRATE);
+ if (bitRate <= 0) {
+ return null;
+ } else if (bitRate < 1000) {
+ return String.format(Locale.US, "%d bit/s", bitRate);
+ } else {
+ return String.format(Locale.US, "%d kb/s", bitRate / 1000);
+ }
+ }
+ });
+ sFormatterMap.put(KEY_IJK_CODEC_PROFILE_LEVEL_UI, new Formatter() {
+ @Override
+ protected String doFormat(IjkMediaFormat mediaFormat) {
+ String profile = mediaFormat.getString(IjkMediaMeta.IJKM_KEY_CODEC_PROFILE);
+ if (TextUtils.isEmpty(profile))
+ return null;
+
+ StringBuilder sb = new StringBuilder();
+ sb.append(profile);
+
+ String codecName = mediaFormat.getString(IjkMediaMeta.IJKM_KEY_CODEC_NAME);
+ if (!TextUtils.isEmpty(codecName) && codecName.equalsIgnoreCase(CODEC_NAME_H264)) {
+ int level = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_CODEC_LEVEL);
+ if (level < 10)
+ return sb.toString();
+
+ sb.append(" Profile Level ");
+ sb.append((level / 10) % 10);
+ if ((level % 10) != 0) {
+ sb.append(".");
+ sb.append(level % 10);
+ }
+ }
+
+ return sb.toString();
+ }
+ });
+ sFormatterMap.put(KEY_IJK_CODEC_PIXEL_FORMAT_UI, new Formatter() {
+ @Override
+ protected String doFormat(IjkMediaFormat mediaFormat) {
+ return mediaFormat.getString(IjkMediaMeta.IJKM_KEY_CODEC_PIXEL_FORMAT);
+ }
+ });
+ sFormatterMap.put(KEY_IJK_RESOLUTION_UI, new Formatter() {
+ @Override
+ protected String doFormat(IjkMediaFormat mediaFormat) {
+ int width = mediaFormat.getInteger(KEY_WIDTH);
+ int height = mediaFormat.getInteger(KEY_HEIGHT);
+ int sarNum = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_SAR_NUM);
+ int sarDen = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_SAR_DEN);
+
+ if (width <= 0 || height <= 0) {
+ return null;
+ } else if (sarNum <= 0 || sarDen <= 0) {
+ return String.format(Locale.US, "%d x %d", width, height);
+ } else {
+ return String.format(Locale.US, "%d x %d [SAR %d:%d]", width,
+ height, sarNum, sarDen);
+ }
+ }
+ });
+ sFormatterMap.put(KEY_IJK_FRAME_RATE_UI, new Formatter() {
+ @Override
+ protected String doFormat(IjkMediaFormat mediaFormat) {
+ int fpsNum = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_FPS_NUM);
+ int fpsDen = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_FPS_DEN);
+ if (fpsNum <= 0 || fpsDen <= 0) {
+ return null;
+ } else {
+ return String.valueOf(((float) (fpsNum)) / fpsDen);
+ }
+ }
+ });
+ sFormatterMap.put(KEY_IJK_SAMPLE_RATE_UI, new Formatter() {
+ @Override
+ protected String doFormat(IjkMediaFormat mediaFormat) {
+ int sampleRate = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_SAMPLE_RATE);
+ if (sampleRate <= 0) {
+ return null;
+ } else {
+ return String.format(Locale.US, "%d Hz", sampleRate);
+ }
+ }
+ });
+ sFormatterMap.put(KEY_IJK_CHANNEL_UI, new Formatter() {
+ @Override
+ protected String doFormat(IjkMediaFormat mediaFormat) {
+ int channelLayout = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_CHANNEL_LAYOUT);
+ if (channelLayout <= 0) {
+ return null;
+ } else {
+ if (channelLayout == IjkMediaMeta.AV_CH_LAYOUT_MONO) {
+ return "mono";
+ } else if (channelLayout == IjkMediaMeta.AV_CH_LAYOUT_STEREO) {
+ return "stereo";
+ } else {
+ return String.format(Locale.US, "%x", channelLayout);
+ }
+ }
+ }
+ });
+ }
+}
diff --git a/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/IjkTrackInfo.java b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/IjkTrackInfo.java
new file mode 100644
index 0000000..f825c27
--- /dev/null
+++ b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/IjkTrackInfo.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2015 Zhang Rui
+ *
+ * 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 tv.danmaku.ijk.media.player.misc;
+
+import android.text.TextUtils;
+
+import tv.danmaku.ijk.media.player.IjkMediaMeta;
+
+public class IjkTrackInfo implements ITrackInfo {
+ private int mTrackType = MEDIA_TRACK_TYPE_UNKNOWN;
+ private IjkMediaMeta.IjkStreamMeta mStreamMeta;
+
+ public IjkTrackInfo(IjkMediaMeta.IjkStreamMeta streamMeta) {
+ mStreamMeta = streamMeta;
+ }
+
+ public void setMediaMeta(IjkMediaMeta.IjkStreamMeta streamMeta) {
+ mStreamMeta = streamMeta;
+ }
+
+ @Override
+ public IMediaFormat getFormat() {
+ return new IjkMediaFormat(mStreamMeta);
+ }
+
+ @Override
+ public String getLanguage() {
+ if (mStreamMeta == null || TextUtils.isEmpty(mStreamMeta.mLanguage))
+ return "und";
+
+ return mStreamMeta.mLanguage;
+ }
+
+ @Override
+ public int getTrackType() {
+ return mTrackType;
+ }
+
+ public void setTrackType(int trackType) {
+ mTrackType = trackType;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + '{' + getInfoInline() + "}";
+ }
+
+ @Override
+ public String getInfoInline() {
+ StringBuilder out = new StringBuilder(128);
+ switch (mTrackType) {
+ case MEDIA_TRACK_TYPE_VIDEO:
+ out.append("VIDEO");
+ out.append(", ");
+ out.append(mStreamMeta.getCodecShortNameInline());
+ out.append(", ");
+ out.append(mStreamMeta.getBitrateInline());
+ out.append(", ");
+ out.append(mStreamMeta.getResolutionInline());
+ break;
+ case MEDIA_TRACK_TYPE_AUDIO:
+ out.append("AUDIO");
+ out.append(", ");
+ out.append(mStreamMeta.getCodecShortNameInline());
+ out.append(", ");
+ out.append(mStreamMeta.getBitrateInline());
+ out.append(", ");
+ out.append(mStreamMeta.getSampleRateInline());
+ break;
+ case MEDIA_TRACK_TYPE_TIMEDTEXT:
+ out.append("TIMEDTEXT");
+ break;
+ case MEDIA_TRACK_TYPE_SUBTITLE:
+ out.append("SUBTITLE");
+ break;
+ default:
+ out.append("UNKNOWN");
+ break;
+ }
+ return out.toString();
+ }
+}
diff --git a/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/pragma/DebugLog.java b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/pragma/DebugLog.java
new file mode 100644
index 0000000..7ccd89d
--- /dev/null
+++ b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/pragma/DebugLog.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2013 Zhang Rui
+ *
+ * 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 tv.danmaku.ijk.media.player.pragma;
+
+import java.util.Locale;
+
+
+import android.util.Log;
+
+@SuppressWarnings({"SameParameterValue", "WeakerAccess"})
+public class DebugLog {
+ public static final boolean ENABLE_ERROR = Pragma.ENABLE_VERBOSE;
+ public static final boolean ENABLE_INFO = Pragma.ENABLE_VERBOSE;
+ public static final boolean ENABLE_WARN = Pragma.ENABLE_VERBOSE;
+ public static final boolean ENABLE_DEBUG = Pragma.ENABLE_VERBOSE;
+ public static final boolean ENABLE_VERBOSE = Pragma.ENABLE_VERBOSE;
+
+ public static void e(String tag, String msg) {
+ if (ENABLE_ERROR) {
+ Log.e(tag, msg);
+ }
+ }
+
+ public static void e(String tag, String msg, Throwable tr) {
+ if (ENABLE_ERROR) {
+ Log.e(tag, msg, tr);
+ }
+ }
+
+ public static void efmt(String tag, String fmt, Object... args) {
+ if (ENABLE_ERROR) {
+ String msg = String.format(Locale.US, fmt, args);
+ Log.e(tag, msg);
+ }
+ }
+
+ public static void i(String tag, String msg) {
+ if (ENABLE_INFO) {
+ Log.i(tag, msg);
+ }
+ }
+
+ public static void i(String tag, String msg, Throwable tr) {
+ if (ENABLE_INFO) {
+ Log.i(tag, msg, tr);
+ }
+ }
+
+ public static void ifmt(String tag, String fmt, Object... args) {
+ if (ENABLE_INFO) {
+ String msg = String.format(Locale.US, fmt, args);
+ Log.i(tag, msg);
+ }
+ }
+
+ public static void w(String tag, String msg) {
+ if (ENABLE_WARN) {
+ Log.w(tag, msg);
+ }
+ }
+
+ public static void w(String tag, String msg, Throwable tr) {
+ if (ENABLE_WARN) {
+ Log.w(tag, msg, tr);
+ }
+ }
+
+ public static void wfmt(String tag, String fmt, Object... args) {
+ if (ENABLE_WARN) {
+ String msg = String.format(Locale.US, fmt, args);
+ Log.w(tag, msg);
+ }
+ }
+
+ public static void d(String tag, String msg) {
+ if (ENABLE_DEBUG) {
+ Log.d(tag, msg);
+ }
+ }
+
+ public static void d(String tag, String msg, Throwable tr) {
+ if (ENABLE_DEBUG) {
+ Log.d(tag, msg, tr);
+ }
+ }
+
+ public static void dfmt(String tag, String fmt, Object... args) {
+ if (ENABLE_DEBUG) {
+ String msg = String.format(Locale.US, fmt, args);
+ Log.d(tag, msg);
+ }
+ }
+
+ public static void v(String tag, String msg) {
+ if (ENABLE_VERBOSE) {
+ Log.v(tag, msg);
+ }
+ }
+
+ public static void v(String tag, String msg, Throwable tr) {
+ if (ENABLE_VERBOSE) {
+ Log.v(tag, msg, tr);
+ }
+ }
+
+ public static void vfmt(String tag, String fmt, Object... args) {
+ if (ENABLE_VERBOSE) {
+ String msg = String.format(Locale.US, fmt, args);
+ Log.v(tag, msg);
+ }
+ }
+
+ public static void printStackTrace(Throwable e) {
+ if (ENABLE_WARN) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void printCause(Throwable e) {
+ if (ENABLE_WARN) {
+ Throwable cause = e.getCause();
+ if (cause != null)
+ e = cause;
+
+ printStackTrace(e);
+ }
+ }
+}
diff --git a/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/pragma/Pragma.java b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/pragma/Pragma.java
new file mode 100644
index 0000000..df26120
--- /dev/null
+++ b/app/libraries/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/pragma/Pragma.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2013 Zhang Rui
+ *
+ * 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 tv.danmaku.ijk.media.player.pragma;
+
+/*-
+ * configurated by app project
+ */
+public class Pragma {
+ public static final boolean ENABLE_VERBOSE = true;
+}
diff --git a/app/libraries/ijkplayer-java/src/main/jniLibs/arm64-v8a/libijkffmpeg.so b/app/libraries/ijkplayer-java/src/main/jniLibs/arm64-v8a/libijkffmpeg.so
new file mode 100644
index 0000000..5b243fb
Binary files /dev/null and b/app/libraries/ijkplayer-java/src/main/jniLibs/arm64-v8a/libijkffmpeg.so differ
diff --git a/app/libraries/ijkplayer-java/src/main/jniLibs/arm64-v8a/libijkplayer.so b/app/libraries/ijkplayer-java/src/main/jniLibs/arm64-v8a/libijkplayer.so
new file mode 100644
index 0000000..8f0f891
Binary files /dev/null and b/app/libraries/ijkplayer-java/src/main/jniLibs/arm64-v8a/libijkplayer.so differ
diff --git a/app/libraries/ijkplayer-java/src/main/jniLibs/arm64-v8a/libijksdl.so b/app/libraries/ijkplayer-java/src/main/jniLibs/arm64-v8a/libijksdl.so
new file mode 100644
index 0000000..3c8ad0d
Binary files /dev/null and b/app/libraries/ijkplayer-java/src/main/jniLibs/arm64-v8a/libijksdl.so differ
diff --git a/app/libraries/ijkplayer-java/src/main/jniLibs/armeabi-v7a/libijkffmpeg.so b/app/libraries/ijkplayer-java/src/main/jniLibs/armeabi-v7a/libijkffmpeg.so
new file mode 100644
index 0000000..bb6271f
Binary files /dev/null and b/app/libraries/ijkplayer-java/src/main/jniLibs/armeabi-v7a/libijkffmpeg.so differ
diff --git a/app/libraries/ijkplayer-java/src/main/jniLibs/armeabi-v7a/libijkplayer.so b/app/libraries/ijkplayer-java/src/main/jniLibs/armeabi-v7a/libijkplayer.so
new file mode 100644
index 0000000..7389549
Binary files /dev/null and b/app/libraries/ijkplayer-java/src/main/jniLibs/armeabi-v7a/libijkplayer.so differ
diff --git a/app/libraries/ijkplayer-java/src/main/jniLibs/armeabi-v7a/libijksdl.so b/app/libraries/ijkplayer-java/src/main/jniLibs/armeabi-v7a/libijksdl.so
new file mode 100644
index 0000000..b4786d7
Binary files /dev/null and b/app/libraries/ijkplayer-java/src/main/jniLibs/armeabi-v7a/libijksdl.so differ
diff --git a/app/libraries/ijkplayer-java/src/main/jniLibs/armeabi/libijkffmpeg.so b/app/libraries/ijkplayer-java/src/main/jniLibs/armeabi/libijkffmpeg.so
new file mode 100644
index 0000000..a7e85d2
Binary files /dev/null and b/app/libraries/ijkplayer-java/src/main/jniLibs/armeabi/libijkffmpeg.so differ
diff --git a/app/libraries/ijkplayer-java/src/main/jniLibs/armeabi/libijkplayer.so b/app/libraries/ijkplayer-java/src/main/jniLibs/armeabi/libijkplayer.so
new file mode 100644
index 0000000..0003292
Binary files /dev/null and b/app/libraries/ijkplayer-java/src/main/jniLibs/armeabi/libijkplayer.so differ
diff --git a/app/libraries/ijkplayer-java/src/main/jniLibs/armeabi/libijksdl.so b/app/libraries/ijkplayer-java/src/main/jniLibs/armeabi/libijksdl.so
new file mode 100644
index 0000000..3ff6335
Binary files /dev/null and b/app/libraries/ijkplayer-java/src/main/jniLibs/armeabi/libijksdl.so differ
diff --git a/app/libraries/ijkplayer-java/src/main/jniLibs/x86/libijkffmpeg.so b/app/libraries/ijkplayer-java/src/main/jniLibs/x86/libijkffmpeg.so
new file mode 100644
index 0000000..32cd1e5
Binary files /dev/null and b/app/libraries/ijkplayer-java/src/main/jniLibs/x86/libijkffmpeg.so differ
diff --git a/app/libraries/ijkplayer-java/src/main/jniLibs/x86/libijkplayer.so b/app/libraries/ijkplayer-java/src/main/jniLibs/x86/libijkplayer.so
new file mode 100644
index 0000000..79eed87
Binary files /dev/null and b/app/libraries/ijkplayer-java/src/main/jniLibs/x86/libijkplayer.so differ
diff --git a/app/libraries/ijkplayer-java/src/main/jniLibs/x86/libijksdl.so b/app/libraries/ijkplayer-java/src/main/jniLibs/x86/libijksdl.so
new file mode 100644
index 0000000..457c156
Binary files /dev/null and b/app/libraries/ijkplayer-java/src/main/jniLibs/x86/libijksdl.so differ
diff --git a/app/libraries/ijkplayer-java/src/main/jniLibs/x86_64/libijkffmpeg.so b/app/libraries/ijkplayer-java/src/main/jniLibs/x86_64/libijkffmpeg.so
new file mode 100644
index 0000000..2742d06
Binary files /dev/null and b/app/libraries/ijkplayer-java/src/main/jniLibs/x86_64/libijkffmpeg.so differ
diff --git a/app/libraries/ijkplayer-java/src/main/jniLibs/x86_64/libijkplayer.so b/app/libraries/ijkplayer-java/src/main/jniLibs/x86_64/libijkplayer.so
new file mode 100644
index 0000000..885c2fd
Binary files /dev/null and b/app/libraries/ijkplayer-java/src/main/jniLibs/x86_64/libijkplayer.so differ
diff --git a/app/libraries/ijkplayer-java/src/main/jniLibs/x86_64/libijksdl.so b/app/libraries/ijkplayer-java/src/main/jniLibs/x86_64/libijksdl.so
new file mode 100644
index 0000000..080157d
Binary files /dev/null and b/app/libraries/ijkplayer-java/src/main/jniLibs/x86_64/libijksdl.so differ
diff --git a/app/libraries/ijkplayer-java/src/main/project.properties b/app/libraries/ijkplayer-java/src/main/project.properties
new file mode 100644
index 0000000..362a0a3
--- /dev/null
+++ b/app/libraries/ijkplayer-java/src/main/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-22
+android.library=true
diff --git a/app/libraries/ijkplayer-java/src/main/res/values/strings.xml b/app/libraries/ijkplayer-java/src/main/res/values/strings.xml
new file mode 100644
index 0000000..7356db3
--- /dev/null
+++ b/app/libraries/ijkplayer-java/src/main/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file