IjkMediaPlayer.java 15.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Copyright (C) 2006 The Android Open Source Project
 * Copyright (C) 2013 Zhang Rui <bbcallen@gmail.com>
 *
 * 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.
 */

Z
Zhang Rui 已提交
18 19 20 21 22 23
package tv.danmaku.ijk.media.player;

import java.io.FileDescriptor;
import java.io.IOException;
import java.lang.ref.WeakReference;

Z
Zhang Rui 已提交
24 25
import tv.danmaku.ijk.media.player.annotations.AccessedByNative;

Z
Zhang Rui 已提交
26 27 28 29 30 31 32 33 34 35
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.view.Surface;
import android.view.SurfaceHolder;

36
public final class IjkMediaPlayer extends AbstractMediaPlayer {
Z
Zhang Rui 已提交
37 38 39
    private final static String TAG = IjkMediaPlayer.class.getName();

    static {
40
        System.loadLibrary("stlport_shared");
41
        System.loadLibrary("ffmpeg");
Z
Zhang Rui 已提交
42 43
        System.loadLibrary("ijkutil");
        System.loadLibrary("ijksdl");
44
        System.loadLibrary("ijkplayer");
Z
Zhang Rui 已提交
45 46 47
        native_init();
    }

Z
Zhang Rui 已提交
48 49 50 51 52 53 54 55 56
    @AccessedByNative
    private long mNativeMediaPlayer;

    @AccessedByNative
    private int mNativeSurfaceTexture;

    @AccessedByNative
    private int mListenerContext;

Z
Zhang Rui 已提交
57 58 59 60 61 62
    private SurfaceHolder mSurfaceHolder;
    private EventHandler mEventHandler;
    private PowerManager.WakeLock mWakeLock = null;
    private boolean mScreenOnWhilePlaying;
    private boolean mStayAwake;

63 64
    private int mVideoWidth;
    private int mVideoHeight;
Z
Zhang Rui 已提交
65 66
    private int mVideoSarNum;
    private int mVideoSarDen;
67

Z
Zhang Rui 已提交
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
    /**
     * Default constructor. Consider using one of the create() methods for
     * synchronously instantiating a IjkMediaPlayer from a Uri or resource.
     * <p>
     * When done with the IjkMediaPlayer, you should call {@link #release()}, to
     * free the resources. If not released, too many IjkMediaPlayer instances
     * may result in an exception.
     * </p>
     */
    public IjkMediaPlayer() {
        Looper looper;
        if ((looper = Looper.myLooper()) != null) {
            mEventHandler = new EventHandler(this, looper);
        } else if ((looper = Looper.getMainLooper()) != null) {
            mEventHandler = new EventHandler(this, looper);
        } else {
            mEventHandler = null;
        }

        /*
         * Native setup requires a weak reference to our object. It's easier to
         * create it here than in C++.
         */
        native_setup(new WeakReference<IjkMediaPlayer>(this));
    }

    /*
     * Update the IjkMediaPlayer SurfaceTexture. Call after setting a new
     * display surface.
     */
    private native void _setVideoSurface(Surface surface);

    /**
     * Sets the {@link SurfaceHolder} to use for displaying the video portion of
     * the media.
     * 
     * Either a surface holder or surface must be set if a display or video sink
     * is needed. Not calling this method or {@link #setSurface(Surface)} when
     * playing back a video will result in only the audio track being played. A
     * null surface holder or surface will result in only the audio track being
     * played.
     * 
     * @param sh
     *            the SurfaceHolder to use for video display
     */
    @Override
    public void setDisplay(SurfaceHolder sh) {
        mSurfaceHolder = sh;
        Surface surface;
        if (sh != null) {
            surface = sh.getSurface();
        } else {
            surface = null;
        }
        _setVideoSurface(surface);
        updateSurfaceScreenOn();
    }

    /**
     * Sets the {@link Surface} to be used as the sink for the video portion of
     * the media. This is similar to {@link #setDisplay(SurfaceHolder)}, but
     * does not support {@link #setScreenOnWhilePlaying(boolean)}. Setting a
     * Surface will un-set any Surface or SurfaceHolder that was previously set.
     * A null surface will result in only the audio track being played.
     * 
     * If the Surface sends frames to a {@link SurfaceTexture}, the timestamps
     * returned from {@link SurfaceTexture#getTimestamp()} will have an
     * unspecified zero point. These timestamps cannot be directly compared
     * between different media sources, different instances of the same media
     * source, or multiple runs of the same program. The timestamp is normally
     * monotonically increasing and is unaffected by time-of-day adjustments,
     * but it is reset when the position is set.
     * 
     * @param surface
     *            The {@link Surface} to be used for the video portion of the
     *            media.
     */
    @Override
    public void setSurface(Surface surface) {
        if (mScreenOnWhilePlaying && surface != null) {
            DebugLog.w(TAG,
                    "setScreenOnWhilePlaying(true) is ineffective for Surface");
        }
        mSurfaceHolder = null;
        _setVideoSurface(surface);
        updateSurfaceScreenOn();
    }

    /**
     * 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
     * 
     *             <p>
     *             When <code>path</code> 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. As an alternative, the
     *             application could first open the file for reading, and then
     *             use the file descriptor form
     *             {@link #setDataSource(FileDescriptor)}.
     */
    @Override
    public void setDataSource(String path) throws IOException,
            IllegalArgumentException, SecurityException, IllegalStateException {
        _setDataSource(path, null, null);
    }

    private native void _setDataSource(String path, String[] keys,
            String[] values) throws IOException, IllegalArgumentException,
            SecurityException, IllegalStateException;

    @Override
    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
267
    public int getVideoWidth() {
268
        return mVideoWidth;
269
    }
Z
Zhang Rui 已提交
270 271

    @Override
272
    public int getVideoHeight() {
273
        return mVideoHeight;
274
    }
Z
Zhang Rui 已提交
275 276 277 278 279

    @Override
    public native boolean isPlaying();

    @Override
280
    public native void seekTo(long msec) throws IllegalStateException;
Z
Zhang Rui 已提交
281 282

    @Override
283
    public native long getCurrentPosition();
Z
Zhang Rui 已提交
284 285

    @Override
286
    public native long getDuration();
Z
Zhang Rui 已提交
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319

    /**
     * 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);
320 321 322

        mVideoWidth = 0;
        mVideoHeight = 0;
Z
Zhang Rui 已提交
323 324 325 326 327
    }

    private native void _reset();

    @Override
328 329 330
    public void setAudioStreamType(int streamtype) {
        // do nothing
    }
Z
Zhang Rui 已提交
331 332 333 334 335 336 337

    private static native final void native_init();

    private native final void native_setup(Object IjkMediaPlayer_this);

    private native final void native_finalize();

Z
Zhang Rui 已提交
338 339
    private native final void native_message_loop(Object IjkMediaPlayer_this);

Z
Zhang Rui 已提交
340 341 342 343 344 345 346 347 348 349 350 351 352 353
    protected void finalize() {
        native_finalize();
    }

    private static class EventHandler extends Handler {
        private IjkMediaPlayer mIjkMediaPlayer;

        public EventHandler(IjkMediaPlayer mp, Looper looper) {
            super(looper);
            mIjkMediaPlayer = mp;
        }

        @Override
        public void handleMessage(Message msg) {
Z
Zhang Rui 已提交
354
            if (mIjkMediaPlayer.mNativeMediaPlayer == 0) {
Z
Zhang Rui 已提交
355 356 357 358 359 360
                DebugLog.w(TAG,
                        "IjkMediaPlayer went away with unhandled events");
                return;
            }
            switch (msg.what) {
            case MEDIA_PREPARED:
361
                notifyOnPrepared(mIjkMediaPlayer);
Z
Zhang Rui 已提交
362 363 364
                return;

            case MEDIA_PLAYBACK_COMPLETE:
365
                notifyOnCompletion(mIjkMediaPlayer);
Z
Zhang Rui 已提交
366 367 368 369
                mIjkMediaPlayer.stayAwake(false);
                return;

            case MEDIA_BUFFERING_UPDATE:
370
                notifyOnBufferingUpdate(mIjkMediaPlayer, msg.arg1);
Z
Zhang Rui 已提交
371 372 373
                return;

            case MEDIA_SEEK_COMPLETE:
374
                notifyOnSeekComplete(mIjkMediaPlayer);
Z
Zhang Rui 已提交
375 376 377
                return;

            case MEDIA_SET_VIDEO_SIZE:
378 379
                mIjkMediaPlayer.mVideoWidth = msg.arg1;
                mIjkMediaPlayer.mVideoHeight = msg.arg2;
Z
Zhang Rui 已提交
380 381 382
                notifyOnVideoSizeChanged(mIjkMediaPlayer, msg.arg1, msg.arg2,
                        mIjkMediaPlayer.mVideoSarNum,
                        mIjkMediaPlayer.mVideoSarDen);
Z
Zhang Rui 已提交
383 384 385 386
                return;

            case MEDIA_ERROR:
                DebugLog.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
387 388
                if (!notifyOnError(mIjkMediaPlayer, msg.arg1, msg.arg2)) {
                    notifyOnCompletion(mIjkMediaPlayer);
Z
Zhang Rui 已提交
389 390 391 392 393 394 395 396
                }
                mIjkMediaPlayer.stayAwake(false);
                return;

            case MEDIA_INFO:
                if (msg.arg1 != MEDIA_INFO_VIDEO_TRACK_LAGGING) {
                    DebugLog.i(TAG, "Info (" + msg.arg1 + "," + msg.arg2 + ")");
                }
397
                notifyOnInfo(mIjkMediaPlayer, msg.arg1, msg.arg2);
Z
Zhang Rui 已提交
398 399 400 401 402 403 404 405 406
                // No real default action so far.
                return;
            case MEDIA_TIMED_TEXT:
                // do nothing
                break;

            case MEDIA_NOP: // interface test message - ignore
                break;

Z
Zhang Rui 已提交
407 408 409 410 411 412 413 414
            case MEDIA_SET_VIDEO_SAR:
                mIjkMediaPlayer.mVideoSarNum = msg.arg1;
                mIjkMediaPlayer.mVideoSarDen = msg.arg2;
                notifyOnVideoSizeChanged(mIjkMediaPlayer, msg.arg1, msg.arg2,
                        mIjkMediaPlayer.mVideoSarNum,
                        mIjkMediaPlayer.mVideoSarDen);
                break;

Z
Zhang Rui 已提交
415 416 417 418 419 420 421 422 423 424 425 426 427 428
            default:
                DebugLog.e(TAG, "Unknown message type " + msg.what);
                return;
            }
        }
    }

    /*
     * 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().)
     */
Z
Zhang Rui 已提交
429 430 431 432 433
    private static void postEventFromNative(Object weakThiz, int what,
            int arg1, int arg2, Object obj) {
        if (weakThiz == null)
            return;

Z
Zhang Rui 已提交
434
        @SuppressWarnings("rawtypes")
Z
Zhang Rui 已提交
435
        IjkMediaPlayer mp = (IjkMediaPlayer) ((WeakReference) weakThiz).get();
Z
Zhang Rui 已提交
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
        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);
        }
    }
}