android ijkplayer adds anti-theft chain refer, screenshot, supports rtsp, modifies the bottom layer, adds screenshot function, and some problems of ijk playback

1: Use the playback mode and screenshot mode of IjkMediaPlayer+SurfaceView

You need to pull your compiled so library from the official website

Pull the Android code git clone https://github.com/Bilibili/ijkplayer.git ijkplayer-android

The compilation steps are the same. There may be some problems with the environment configuration. ndk recommends r14; Too low to compile, too high java version does not support;

If you package a player yourself, you can directly inherit the Surface and implement the methods that need to be inherited,

1: Initialization

Initialize in the constructor and set the playback screen to the Surface

    public BlibiliVideoPlayer(Context context) {
        super(context);
        init();
    }

         

    private void init() {
        IjkMediaPlayer.loadLibrariesOnce(null);
        IjkMediaPlayer.native_profileBegin("libijkplayer.so");
        ijkMediaPlayer=new IjkMediaPlayer();
        //Open log
//        ijkMediaPlayer.native_setLogLevel(IjkMediaPlayer.IJK_LOG_DEBUG);

        getHolder().addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                ijkMediaPlayer.setDisplay(holder);
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
                ijkMediaPlayer.setDisplay(holder);
//                ijkMediaPlayer.prepareAsync();
//                loadVideo();
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                if(ijkMediaPlayer!=null){
                    ijkMediaPlayer.setDisplay(null);
                }
            }

        });
        ijkMediaPlayer.setOnSeekCompleteListener(new IMediaPlayer.OnSeekCompleteListener() {
            @Override
            public void onSeekComplete(IMediaPlayer iMediaPlayer) {
                inSeek = false;
            }
        });
        ijkMediaPlayer.setOnCompletionListener(new IMediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(IMediaPlayer iMediaPlayer) {
                inSeek = false;
                //off timer 
                stopProgressUpdateTimer();
                try{
                    if(null != listener) {
                        listener.completed();
                    }
                }catch (Exception e){

                }

            }
        });
        ijkMediaPlayer.setOnInfoListener(new IMediaPlayer.OnInfoListener() {
            @Override
            public boolean onInfo(IMediaPlayer iMediaPlayer, int i, int i1) {
                if(null != listener) {
                    listener.info(VideoPlayerConstants.ALI,i,0);
                }
                return false;
            }
        });
        ijkMediaPlayer.setOnPreparedListener(new IMediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(IMediaPlayer iMediaPlayer) {
                prepared = true;
                if(null != listener){
                    listener.prepared();
                }
            }
        });

        //
        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_clear", 1);
        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER,
                "http-detect-range-support", 1);

//        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_timeout", -1);
//        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 0);
//        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", IjkMediaPlayer.SDL_FCC_RV32);
//        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 1);
//        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0);
//        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0);
//        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "http-detect-range-support", 0);
//        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT,"safe",0);
//        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "protocol_whitelist", "hls,async,cache,crypto,file,http,https,ijkhttphook,ijkinject,ijklivehook,ijklongurl,ijksegment,ijktcphook,pipe,rtp,tcp,tls,udp,ijkurlhook,data,concat,subfile,udp,ffconcat");
//
//        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48);
        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);
//        ijkMediaPlayer.
//        ijkMediaPlayer.setOnVideoDecoderErrorListener(new IMediaPlayer.OnVideoDecoderErrorListener() {
//            @Override
//            public void onVideoDecoderError(IMediaPlayer iMediaPlayer) {
//                Log.d("log", "decoding error");
//            }
//        });
    }
private void loadVideo() {
        ijkMediaPlayer.prepareAsync();
        ijkMediaPlayer.setDisplay(getHolder());
        ijkMediaPlayer.start();
//        startProgressUpdateTimer();
}

Add request header, anti-theft chain and refer:

Map<String, String> headers = new HashMap<>();
headers.put("Referer","content");//
if (headers != null) {
    try{
        ijkMediaPlayer.setDataSource(url,headers);
     }catch (Exception e){

     }
     }else{
     try{
        ijkMediaPlayer.setDataSource(url);
     }catch (Exception e){

    }
}//Playback mode with and without header

You can play it normally here.

2 screenshot implementation: need to change to FF under ijkplayer Android / ijkmedia / ijkplayer_ ffplay. c        ijkplayer.c        ff_ffplay-def.h file, Android MK and ijkplayer of ijkplayer Android / ijkmedia / ijkplayer / Android /_ jni. c

JNI add: ijkplayer_jni.c

#include <android/bitmap. h> / / this package needs to be imported


static jboolean
IjkMediaPlayer_getCurrentFrame(JNIEnv *env, jobject thiz, jobject bitmap)
{
	jboolean retval = JNI_TRUE;
	IjkMediaPlayer *mp = jni_get_media_player(env, thiz);
	JNI_CHECK_GOTO(mp, env, NULL, "mpjni: getCurrentFrame: null mp", LABEL_RETURN);

	uint8_t *frame_buffer = NULL;

	if (0 > AndroidBitmap_lockPixels(env, bitmap, (void **)&frame_buffer)) {
		(*env)->ThrowNew(env, "java/io/IOException", "Unable to lock pixels.");
		return JNI_FALSE;
	}

	ijkmp_get_current_frame(mp, frame_buffer);

	if (0 > AndroidBitmap_unlockPixels(env, bitmap)) {
		(*env)->ThrowNew(env, "java/io/IOException", "Unable to unlock pixels.");
		return JNI_FALSE;
	}/Users/hoge/ijkplayer-android/ijkmedia/ijkplayer/android/ijkplayer_jni.c

	LABEL_RETURN:
	ijkmp_dec_ref_p(&mp);
    return retval;
}


JNINativeMethod Add in

{ "getCurrentFrame", "(Landroid/graphics/Bitmap;)Z", (void *) IjkMediaPlayer_getCurrentFrame },//Add this
{ "_setOption",             "(ILjava/lang/String;Ljava/lang/String;)V", (void *) IjkMediaPlayer_setOption },//This one already exists

Android.mk:

include $(CLEAR_VARS)
# -mfloat-abi=soft is a workaround for FP register corruption on Exynos 4210
# http://www.spinics.net/lists/arm-kernel/msg368417.html
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
LOCAL_CFLAGS += -mfloat-abi=soft
endif
LOCAL_CFLAGS += -std=c99
LOCAL_LDLIBS += -llog -landroid -ljnigraphics//Add this -ljnigraphics

ijkplayer.c:

static void ijkmp_get_current_frame_l(IjkMediaPlayer *mp, uint8_t *frame_buf)
{
	ffp_get_current_frame_l(mp->ffplayer, frame_buf);
}

void ijkmp_get_current_frame(IjkMediaPlayer *mp, uint8_t *frame_buf)
{
	assert(mp);
	pthread_mutex_lock(&mp->mutex);
	ijkmp_get_current_frame_l(mp, frame_buf);
	pthread_mutex_unlock(&mp->mutex);
}

ff_ffplay.c

void ffp_get_current_frame_l(FFPlayer *ffp, uint8_t *frame_buf)
{
	ALOGD("=============>start snapshot\n");

	VideoState *is = ffp->is;
	Frame *vp;
	int i = 0, linesize = 0, pixels = 0;
	uint8_t *src;

	vp = &is->pictq.queue[is->pictq.rindex];
	int height = vp->bmp->h;
	int width = vp->bmp->w;

	ALOGD("=============>%d X %d === %d\n", width, height, vp->bmp->pitches[0]);

	// copy data to bitmap in java code
	linesize = vp->bmp->pitches[0];
	src = vp->bmp->pixels[0];
	pixels = width * 4;
	for (i = 0; i < height; i++) {
		memcpy(frame_buf + i * pixels, src + i * linesize, pixels);
	}

	ALOGD("=============>end snapshot\n");
}

ff_ffplay-def.h:

typedef struct FFPlayer {

//Add these variables to this method and put them at the beginning
 AVFormatContext *m_ofmt_ctx;
    AVOutputFormat *m_ofmt;
    pthread_mutex_t record_mutex;
    char *screen_file_name;
    int is_record;
    int record_error;
    int is_first;
    int is_screenshot;//Is the screenshot one-time? Only one screenshot can be taken at a time
    int64_t start_pts;
    int64_t start_dts;

}

After modification, start compiling and execute in jkplayer Android / Android / contrib directory

./compile-ffmpeg.sh clean
 ./ compile-ffmpeg.sh armv7a / / armv7a can be changed to what you want, and all of them can be compiled

Then cd Go to the previous directory jkplayer Android / Android

./ compile-ijk.sh armv7a / / generate so file

The generated so is in the corresponding file directory. For example, the armv7a compiled here is in the ijkplay Android / Android / ijkplay / ijkplay armv7a / SRC / main / LIBS / armeabi v7a directory

tips:

If you have compiled once, you can modify these c files again without executing the above steps

./ compile-ffmpeg.sh clean, which can greatly speed up the compilation speed

2: Add a screenshot method to java,

Put the generated so file into the project and modify imedia player java:

add to

boolean getCurrentFrame(Bitmap bitmap); method

Because there are many files that inherit the imedia player Java. After adding this, you need to implement the getCurrentFrame in the subclass that inherits it, such as ijkmediaplayer Adding nativa method to Java

@Override
public native boolean getCurrentFrame(Bitmap bitmap);

There are several files Oh, remember to add them;

Screenshot implementation:

if(ijkMediaPlayer!=null){
            int width = getVideoWidth();//Desired width
            int height = getVideoHeight();//Desired height
            Bitmap srcBitmap = Bitmap.createBitmap(width,
                    height, Bitmap.Config.ARGB_4444);//Recommendation 4444 is relatively small, and 8888 reports missing once
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddhhmmss");
            String picName = dateFormat.format(new java.util.Date());
            //Generate picture files by date
            //Util.getScreenShotPath() is the path, which is usually placed under data / package name / directory
            //If this path does not exist, an error will be reported. If you do not have read-write permission, you will also report an error. How to apply for permission yourself Baidu
            String mPath2 = Util.getScreenShotPath() + picName + ".png";
            boolean flag = ijkMediaPlayer.getCurrentFrame(srcBitmap);
            if (flag) {
                File imageFile = new File(mPath2);
                try {
                    if(imageFile!=null){
                        //Save picture
                        OutputStream fout = new FileOutputStream(imageFile);
                        srcBitmap.compress(Bitmap.CompressFormat.PNG, 100, fout);
                        fout.flush();
                        fout.close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    callback.onFaild();
                }
            }
        }

3: Problem record

1. Failed to play the m3u8 stream of https, and failed to request the internal ts file. The problem is that the stream is an https protocol, but the ts file in the m3u8 stream is http. If the protocol switching fails, you can change it to HTTP play, or change the ts file in the m3u8 stream to https in the background, Anyway, it is the same as the beginning of this m3u8 stream. On my side, the cdn has converted all HTTP into https, so I didn't handle it

2. Playing https stream will fail after switching to http

Questions on official git https://github.com/bilibili/ijkplayer/issues/4905

The solution given is to set up dns cleaning, but visual inspection is useless. It doesn't work after switching once

ijkMediaPlayer.setOption( IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_clear", 1);

Summary of others:

Summary of problems encountered in ijkplayer - Nuggets

Well, that's it. If there are any omissions or problems, leave a comment. Thank you 🙏

Keywords: Java Android

Added by xiosen on Thu, 30 Dec 2021 11:21:57 +0200