android ijkplayer添加防盗链refer,截图,支持rtsp,修改底层增加截图功能,以及ijk播放的部分问题

一:使用IjkMediaPlayer+SurfaceView的播放方式,和截图方式

        需要到官网拉取自己编译的so库

        拉取安卓代码 git clone https://github.com/Bilibili/ijkplayer.git ijkplayer-android

编译步骤百度搜索一下吧,都一样,可能会有些环境配置的问题,ndk推荐使用r14的版本;太低编译不过去,太高java版本不支持;

自己封装一个播放器的话,直接继承Surface ,实现需要继承的方法,

1:初始化

在构造函数中初始化,设置播放画面到Surface

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

         

    private void init() {
        IjkMediaPlayer.loadLibrariesOnce(null);
        IjkMediaPlayer.native_profileBegin("libijkplayer.so");
        ijkMediaPlayer=new IjkMediaPlayer();
        //开启日志
//        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;
                //关闭定时器
                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","解码错误");
//            }
//        });
    }
private void loadVideo() {
        ijkMediaPlayer.prepareAsync();
        ijkMediaPlayer.setDisplay(getHolder());
        ijkMediaPlayer.start();
//        startProgressUpdateTimer();
}

添加请求头,防盗链,refer的方式:

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

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

    }
}//有header和没header的播放方式

到这里就可以正常播放啦。

2截图实现:需要改到ijkplayer-android/ijkmedia/ijkplayer下的ff_ffplay.c        ijkplayer.c        ff_ffplay-def.h文件,Android.mk以及ijkplayer-android/ijkmedia/ijkplayer/android/的ijkplayer_jni.c

jni添加:ijkplayer_jni.c

#include <android/bitmap.h>//需要引入这个包


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中添加

{ "getCurrentFrame", "(Landroid/graphics/Bitmap;)Z", (void *) IjkMediaPlayer_getCurrentFrame },//添加这条
{ "_setOption",             "(ILjava/lang/String;Ljava/lang/String;)V", (void *) IjkMediaPlayer_setOption },//这条已经有了

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//添加这个-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 {

//在这个方法里添加这些变量,就放在最开头吧
 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;//是否截图,是一次性的,一次只能截一张图
    int64_t start_pts;
    int64_t start_dts;

}

修改完毕开始编译,在jkplayer-android/android/contrib目录下执行

./compile-ffmpeg.sh clean
 ./compile-ffmpeg.sh armv7a                //armv7a可以改成想要的,活着all编译全部

然后cd .. 到上一个目录 jkplayer-android/android下执行

./compile-ijk.sh armv7a//生成so文件

生成的so在对应的文件目录下,比如这边编译的armv7a就在ijkplayer-android/android/ijkplayer/ijkplayer-armv7a/src/main/libs/armeabi-v7a目录下

tips:

如果有编译过一次了,再次修改上面的这些c文件就可以不用再执行上面的

./compile-ffmpeg.sh clean,可以大大加快编译速度

二:java 添加截图方法,

        把生成的so文件放到项目中,修改IMediaPlayer.java:

添加

boolean getCurrentFrame(Bitmap bitmap);方法

因为有好多个文件继承这个IMediaPlayer.java,添加这个后需要到继承他的子类中实现这个getCurrentFrame,比如IjkMediaPlayer.java中添加nativa方法

@Override
public native boolean getCurrentFrame(Bitmap bitmap);

有好几个文件哦,记得都添加上;

截图实现:

if(ijkMediaPlayer!=null){
            int width = getVideoWidth();//想要的宽度
            int height = getVideoHeight();//想要的高度
            Bitmap srcBitmap = Bitmap.createBitmap(width,
                    height, Bitmap.Config.ARGB_4444);//建议4444比较小,8888报错过一次
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddhhmmss");
            String picName = dateFormat.format(new java.util.Date());
            //按照日期生成图片文件
            //Util.getScreenShotPath() 是路径,一般放data/包名/目录下.
            //这个路径不存在会报错,没读写权限也会报错,怎么申请权限自己百度
            String mPath2 = Util.getScreenShotPath() + picName + ".png";
            boolean flag = ijkMediaPlayer.getCurrentFrame(srcBitmap);
            if (flag) {
                File imageFile = new File(mPath2);
                try {
                    if(imageFile!=null){
                        //保存图片
                        OutputStream fout = new FileOutputStream(imageFile);
                        srcBitmap.compress(Bitmap.CompressFormat.PNG, 100, fout);
                        fout.flush();
                        fout.close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    callback.onFaild();
                }
            }
        }

三:问题记录

        1,播放https的m3u8流失败,请求内部的ts文件失败,没有解决这个问题,问题点在于这个流是https的协议,但是这个m3u8流里面的ts文件是http的,切换协议失败,可以改成http播放,或者后台把这个m3u8流里面的ts文件都改成https,反正就是要跟这个m3u8流一样的开头,我这边是cdn上有把http的都转换成https,所以就没处理了    android ijkplayer添加防盗链refer,截图,支持rtsp,修改底层增加截图功能,以及ijk播放的部分问题

        2播放https的流切换到http后会播放失败 

        官方git上的提问https://github.com/bilibili/ijkplayer/issues/4905

        给出的解决方案是设置清理dns,但是目测没啥用,切换一次后又不行了

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

别人的汇总:

ijkplayer中遇到的问题汇总 - 掘金

好啦到这里就完结了,有哪里有遗漏或者问题的话留个评论,谢谢

上一篇:python opencv 摄像头


下一篇:记录开发一个轮播小组件用到的一个解决较复杂问题的思路