ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-12-29 18:58:16  阅读:359  来源: 互联网

标签:截图 ijkMediaPlayer ijkplayer setOption frame ijk void 防盗链 IjkMediaPlayer


一:使用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,所以就没处理了    

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

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

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

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

别人的汇总:

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

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

标签:截图,ijkMediaPlayer,ijkplayer,setOption,frame,ijk,void,防盗链,IjkMediaPlayer
来源: https://blog.csdn.net/weixin_61230884/article/details/122220419

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有