ijkplayer编译,支持https,rtsp,直播录制与截图功能

直接使用可在android-aar目录下自取aar
有其它需求可拉取源码后再次编译

demo地址:https://github.com/zzhengzhe/ijkplayer.git

  • ijk编译流程

1.安装git 和 yasm

sudo apt-get install git
sudo apt-get install yasm

2.配置sdk和ndk环境变量

注意:ndk使用最新r23b版本后期编译报错,建议直接下载r10e版本https://dl.google.com/android/repository/android-ndk-r10e-linux-x86_64.zip
打开.bashrc文件,配置环境
验证ndk配置是否成功:ndk-build -v

 3.获取ijkplayer源码

git clone https://github.com/Bilibili/ijkplayer.git ijkplayer-android
cd ijkplayer-android
git checkout -B latest k0.8.8

4. 初始化

./init-android.sh

5.修改module-lite.sh支持rtsp

export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=rtp"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=rtsp"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=mjpeg"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=mjpeg"

cd config
rm module.sh
ln -s module-lite.sh module.sh
source module.sh

 6.增加录制和截图功能

路径:ijkmedia/ijkplayer/Android.mk

//修改
//LOCAL_LDLIBS += -llog -landroid
LOCAL_LDLIBS += -llog -landroid -ljnigraphics

路径:ijkmedia/ijkplayer/android/ijkplayer_jni.c

#include "ijksdl/android/ijksdl_android_jni.h"
//新增
#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;}LABEL_RETURN:ijkmp_dec_ref_p(&mp);return retval;
}static jint
IjkMediaPlayer_startRecord(JNIEnv *env, jobject thiz,jstring file)
{jint retval = 0;IjkMediaPlayer *mp = jni_get_media_player(env, thiz);JNI_CHECK_GOTO(mp, env, NULL, "mpjni: startRecord: null mp", LABEL_RETURN);const char *nativeString = (*env)->GetStringUTFChars(env, file, 0);retval = ijkmp_start_record(mp,nativeString);LABEL_RETURN:ijkmp_dec_ref_p(&mp);return retval;
}static jint
IjkMediaPlayer_stopRecord(JNIEnv *env, jobject thiz)
{jint retval = 0;IjkMediaPlayer *mp = jni_get_media_player(env, thiz);JNI_CHECK_GOTO(mp, env, NULL, "mpjni: stopRecord: null mp", LABEL_RETURN);retval = ijkmp_stop_record(mp);LABEL_RETURN:ijkmp_dec_ref_p(&mp);return retval;
}
    { "getDuration",            "()J",      (void *) IjkMediaPlayer_getDuration },//新增{ "getCurrentFrame",        "(Landroid/graphics/Bitmap;)Z",      (void *) IjkMediaPlayer_getCurrentFrame },{ "startRecord",            "(Ljava/lang/String;)I",      (void *) IjkMediaPlayer_startRecord },{ "stopRecord",             "()I",      (void *) IjkMediaPlayer_stopRecord },

路径:ijkmedia/ijkplayer/ff_ffplay.c

//修改
static int read_thread(void *arg)
{/* offset should be seeked*/if (ffp->seek_at_start > 0) {ffp_seek_to_l(ffp, (long)(ffp->seek_at_start));}for (;;) {
//插入开始if (!ffp->is_first && pkt->pts == pkt->dts) { // 获取开始录制前dts等于pts最后的值,用于if (pkt->stream_index == AVMEDIA_TYPE_AUDIO) {ffp->start_a_pts = pkt->pts;ffp->start_a_dts = pkt->dts;}}if (pkt->stream_index == AVMEDIA_TYPE_VIDEO) {if (!ffp->is_first) {ffp->start_v_pts = pkt->pts;ffp->start_v_dts = pkt->dts;//                        printf("set start video start_pts: %lld +++++ start_dts: %lld\n", ffp->start_v_pts, ffp->start_v_dts);}}if (pkt->stream_index == AVMEDIA_TYPE_VIDEO) {//                    printf("video pts: %lld +++++ dts: %lld\n", pkt->pts, pkt->dts);}
#pragma  mark - 录制插入if (ffp->is_record) { // 可以录制时,写入文件if (0 != ffp_record_file(ffp, pkt)) {ffp->record_error = 1;ffp_stop_recording_l(ffp);}}
//插入结束    if (is->abort_request)break;
#ifdef FFP_MERGE}
//新增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 codelinesize = 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");
}int ffp_start_recording_l(FFPlayer *ffp, const char *file_name)
{assert(ffp);VideoState *is = ffp->is;ffp->m_ofmt_ctx = NULL;ffp->m_ofmt = NULL;ffp->is_record = 0;ffp->record_error = 0;if (!file_name || !strlen(file_name)) { // 没有路径av_log(ffp, AV_LOG_ERROR, "filename is invalid");goto end;}if (!is || !is->ic|| is->paused || is->abort_request) { // 没有上下文,或者上下文已经停止av_log(ffp, AV_LOG_ERROR, "is,is->ic,is->paused is invalid");goto end;}if (ffp->is_record) { // 已经在录制av_log(ffp, AV_LOG_ERROR, "recording has started");goto end;}// 初始化一个用于输出的AVFormatContext结构体avformat_alloc_output_context2(&ffp->m_ofmt_ctx, NULL, "mp4", file_name);if (!ffp->m_ofmt_ctx) {av_log(ffp, AV_LOG_ERROR, "Could not create output context filename is %s\n", file_name);goto end;}ffp->m_ofmt = ffp->m_ofmt_ctx->oformat;for (int i = 0; i < is->ic->nb_streams; i++) {// 对照输入流创建输出流通道AVStream *in_stream = is->ic->streams[i];AVStream *out_stream = avformat_new_stream(ffp->m_ofmt_ctx, in_stream->codec->codec);if (!out_stream) {av_log(ffp, AV_LOG_ERROR, "Failed allocating output stream\n");goto end;}// 将输入视频/音频的参数拷贝至输出视频/音频的AVCodecContext结构体av_log(ffp, AV_LOG_DEBUG, "in_stream->codec;%@\n", in_stream->codec);if (avcodec_copy_context(out_stream->codec, in_stream->codec) < 0) {av_log(ffp, AV_LOG_ERROR, "Failed to copy context from input to output stream codec context\n");goto end;}out_stream->codec->codec_tag = 0;if (ffp->m_ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) {out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;}}av_dump_format(ffp->m_ofmt_ctx, 0, file_name, 1);// 打开输出文件if (!(ffp->m_ofmt->flags & AVFMT_NOFILE)) {if (avio_open(&ffp->m_ofmt_ctx->pb, file_name, AVIO_FLAG_WRITE) < 0) {av_log(ffp, AV_LOG_ERROR, "Could not open output file '%s'", file_name);goto end;}}// 写视频文件头if (avformat_write_header(ffp->m_ofmt_ctx, NULL) < 0) {av_log(ffp, AV_LOG_ERROR, "Error occurred when opening output file\n");goto end;}ffp->is_record = 1;ffp->record_error = 0;pthread_mutex_init(&ffp->record_mutex, NULL);return 0;
end:ffp->record_error = 1;return -1;
}int ffp_record_file(FFPlayer *ffp, AVPacket *packet)
{assert(ffp);VideoState *is = ffp->is;int ret = 0;AVStream *in_stream;AVStream *out_stream;if (ffp->is_record) {if (packet == NULL) {ffp->record_error = 1;av_log(ffp, AV_LOG_ERROR, "packet == NULL");return -1;}AVPacket *pkt = (AVPacket *)av_malloc(sizeof(AVPacket)); // 与看直播的 AVPacket分开,不然卡屏av_new_packet(pkt, 0);if (0 == av_packet_ref(pkt, packet)) {pthread_mutex_lock(&ffp->record_mutex);if (!ffp->is_first) { // 录制的第一帧,时间从0开始ffp->is_first = 1;pkt->pts = 0;pkt->dts = 0;} else { // 之后的每一帧都要减去,点击开始录制时的值,这样的时间才是正确的//pkt->pts = abs(pkt->pts - ffp->start_pts);//pkt->dts = abs(pkt->dts - ffp->start_dts);if (pkt->stream_index == AVMEDIA_TYPE_AUDIO) {pkt->pts = llabs(pkt->pts - ffp->start_a_pts);pkt->dts = llabs(pkt->dts - ffp->start_a_dts);}else if (pkt->stream_index == AVMEDIA_TYPE_VIDEO) {pkt->pts = pkt->dts = llabs(pkt->dts - ffp->start_v_dts);}}in_stream  = is->ic->streams[pkt->stream_index];out_stream = ffp->m_ofmt_ctx->streams[pkt->stream_index];// 转换PTS/DTSpkt->pts = av_rescale_q_rnd(pkt->pts, in_stream->time_base, out_stream->time_base, (AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));pkt->dts = av_rescale_q_rnd(pkt->dts, in_stream->time_base, out_stream->time_base, (AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));pkt->duration = av_rescale_q(pkt->duration, in_stream->time_base, out_stream->time_base);pkt->pos = -1;// 写入一个AVPacket到输出文件if ((ret = av_interleaved_write_frame(ffp->m_ofmt_ctx, pkt)) < 0) {av_log(ffp, AV_LOG_ERROR, "Error muxing packet\n");}av_packet_unref(pkt);pthread_mutex_unlock(&ffp->record_mutex);} else {av_log(ffp, AV_LOG_ERROR, "av_packet_ref == NULL");}}return ret;
}int ffp_record_isfinished_l(FFPlayer *ffp)
{return 0;
}int ffp_stop_recording_l(FFPlayer *ffp)
{assert(ffp);if (ffp->is_record) {ffp->is_record = 0;pthread_mutex_lock(&ffp->record_mutex);if (ffp->m_ofmt_ctx != NULL) {av_write_trailer(ffp->m_ofmt_ctx);if (ffp->m_ofmt_ctx && !(ffp->m_ofmt->flags & AVFMT_NOFILE)) {avio_close(ffp->m_ofmt_ctx->pb);}avformat_free_context(ffp->m_ofmt_ctx);ffp->m_ofmt_ctx = NULL;ffp->is_first = 0;}pthread_mutex_unlock(&ffp->record_mutex);pthread_mutex_destroy(&ffp->record_mutex);av_log(ffp, AV_LOG_DEBUG, "stopRecord ok\n");} else {av_log(ffp, AV_LOG_ERROR, "don't need stopRecord\n");}return 0;
} 

路径:ijkmedia/ijkplayer/ff_ffplay.h

void      ffp_set_property_int64(FFPlayer *ffp, int id, int64_t value);
//新增
int       ffp_start_recording_l(FFPlayer *ffp, const char *file_name);
int       ffp_stop_recording_l(FFPlayer *ffp);
int       ffp_record_file(FFPlayer *ffp, AVPacket *packet);
void      ffp_get_current_frame_l(FFPlayer *ffp, uint8_t *frame_buf);
int       ffp_record_isfinished_l(FFPlayer *ffp);

路径:ijkmedia/ijkplayer/ff_ffplay_def.h

    int render_wait_start;//新增AVFormatContext *m_ofmt_ctx;        // 用于输出的AVFormatContext结构体AVOutputFormat *m_ofmt;pthread_mutex_t record_mutex;       // 锁int is_record;                      // 是否在录制int record_error;int is_first;                       // 第一帧数据int64_t start_v_pts;                // 开始录制时pts 视频int64_t start_v_dts;                // 开始录制时dts 视频int64_t start_a_pts;                // 开始录制时pts 音频int64_t start_a_dts;                // 开始录制时dts 音频

路径:ijkmedia/ijkplayer/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);
}static int ijkmp_start_recording_l(IjkMediaPlayer *mp,const char *filePath)
{
//  av_log(mp->ffplayer,AV_LOG_WARING,"cjz ijkmp_start_recording_l filePath %s",filePath);return ffp_start_recording_l(mp->ffplayer,filePath);
}int ijkmp_start_record(IjkMediaPlayer *mp,const char *filePath)
{assert(mp);pthread_mutex_lock(&mp->mutex);av_log(mp->ffplayer,AV_LOG_WARNING,"cjz ijkmp_start_recording");int retval = ijkmp_start_recording_l(mp,filePath);pthread_mutex_unlock(&mp->mutex);return retval;
}static int ijkmp_stop_recording_l(IjkMediaPlayer *mp)
{return ffp_stop_recording_l(mp->ffplayer);
} int ijkmp_stop_record(IjkMediaPlayer *mp)
{assert(mp);pthread_mutex_lock(&mp->mutex);av_log(mp->ffplayer,AV_LOG_WARNING,"cjz ijkmp_stop_recording");int retval = ijkmp_stop_recording_l(mp);pthread_mutex_unlock(&mp->mutex);return retval;
}static int ijkmp_isRecordFinished_l(IjkMediaPlayer *mp)
{return ffp_record_isfinished_l(mp->ffplayer);
}int ijkmp_isRecordFinished(IjkMediaPlayer *mp)
{assert(mp);pthread_mutex_lock(&mp->mutex);av_log(mp->ffplayer,AV_LOG_WARNING,"cjz ijkmp_isRecordFinished ");int retval = ijkmp_isRecordFinished_l(mp);pthread_mutex_unlock(&mp->mutex);return retval;
}int ijkmp_isRecording(IjkMediaPlayer *mp) {return mp->ffplayer->is_record;
}

7.支持Https

cd ..
./init-android-openssl.sh

8.清除

cd android/contrib
./compile-openssl.sh clean
./compile-ffmpeg.sh clean

9.编译openssl

./compile-openssl.sh all

10.编译ffmpeg

./compile-ffmpeg.sh all

11.编译ijkplayer

cd ..
./compile-ijk.sh all

  • aar打包流程

编译成功后会在android目录下生成一个ijkplayer的工程,导入AS

  1. 右键项目 Open Module Settings,点击减号移除ijkplayer-example和ijkplayer-java以外的依赖

  2. 增加需要支持的.so,打开ijkplayer-java/src/main/,新建一个libs文件夹,同时打开ijkplayer-xxx/main/libs,把里面的armeabi-v7a,arm64-v8a,armeabi三个文件夹复制到ijkplayer-java的libs文件夹下

  3. 修改IMediaPlayer

    //新增int startRecord(String file);int stopRecord();boolean getCurrentFrame(Bitmap bitmap);
  4. 修改IjkMediaPlayer/AndroidMediaPlayer/MediaPlayerProxy,新增native方法

        @Overridepublic native int startRecord(String file);@Overridepublic native int stopRecord();@Overridepublic native boolean getCurrentFrame(Bitmap bitmap);
    
  5. 修改下ijkplayer-java的build.gradle文件,编译arr

    gradle -- ijkplayer-java – Tasks – build -- assemble

ijkplayer编译生成aar,支持https,rtsp,录制与截图相关推荐

  1. IJKPlayer播放器支持https流程

    前言: 笔者最近一段时间在搞视频直播相关的东西,虽然很早以前,也做过基于腾讯云.Anychat.容联云等几个第三方的实时视频直播,但是当时的播放器完全采用了第三方的东西,本次则是使用的ijkplaye ...

  2. ijkplayer 视频播放器集成(附带支持https的so库)

    前言 做一款视频播放器需要多久?有了ijkplayer只需要简单几步就能快速搞出来一个功能强大播放器. github链接:ijkplayer Android: Gradlerequired allpr ...

  3. loadrunner支持https协议的操作方法-经验总结

    问题:用户portal支持https协议,用loadrunner录制登陆脚本时发现未录制到用户名和密码 录制到的脚本如下: login() { lr_think_time(10); web_url(& ...

  4. 新功能:阿里云负载均衡SLB支持HTTPS虚拟主机功能(SNI)

    Greeting 大家好,很高兴告诉大家,阿里云负载均衡SLB支持HTTPS虚拟主机(单VIP多证书功能-SNI),目前已经在所有地域开放,欢迎大家使用.具体内容请大家观看视频. 温馨提醒:视频杀流量 ...

  5. (0009) iOS 开发之友盟统计分析SDK已全面支持HTTPS的更新

    iOS开发技术分享群 147787076 [友盟+]友盟统计分析SDK已全面支持HTTPS,请IOS开发者及时升级文档(http://dev.umeng.com/analytics/ios-doc/i ...

  6. curl提示不支持https协议解决方法

    curl提示不支持https协议解决方法 参考文章: (1)curl提示不支持https协议解决方法 (2)https://www.cnblogs.com/biaopei/p/8669810.html ...

  7. go get 代理 找不到包_初步看看Go1.10 支持 HTTPS 代理

    Go1.10 支持 HTTPS 代理 Go1.9 出来后 6 个多月的时间,Go1.10 就被发布.新版本带来大大小小的变化(发行说明),但是我想谈谈有关 net/http 包的改变.1.10 版本支 ...

  8. TurboMail邮件系统支持HTTPS双向认证

    2019独角兽企业重金招聘Python工程师标准>>> HTTP单向认证已经被普遍应用,而对企业邮箱安全保密要求更加严格的企事业单位,例如国家保密局等单位,为了达到闭环的安全加密要求 ...

  9. php cur https,解决php-curl不支持https问题

    主要原因是因为之前安装的 curl 不支持 https 协议,导致 php-curl 也不支持 https 协议 #/server/app/services/bin/curl -V   查看curl ...

最新文章

  1. 科研费4年翻3倍,全球科研队伍突破8000人,滴滴致力打造出行领域核心技术
  2. oracle中的赋权
  3. 亚马逊消费者业务宣布永久关闭 Oracle 数据库,去O新进展;华为发布最新5G全系列解决方案;苹果正研究新设备“智能戒指”……...
  4. Linux Shell编程之别名和常用快捷键
  5. laravel接口开发JWTAuth
  6. django 1.8 官方文档翻译: 3-4-1 基于类的视图
  7. extract()函数:用于从一个date或者interval类型中截取到特定的部分
  8. [转载] numpy功能快速查找
  9. 2021L3HCTF luuuuua Writeup
  10. 解决IAR printf函数输出中文字符乱码问题
  11. Linked Data_百度百科
  12. module java.base does not “opens java.lang“ to module spring.core
  13. TTL传输中过期问题导致网站打不开
  14. android 方向传感器,10.11 传感器专题(2)——方向传感器
  15. 推荐一个赚钱平台,100%能赚钱(合法 长久 稳定)
  16. rdkafka线程过多_我是如何处理大并发量订单处理的 KafKa部署总结
  17. linux top VIRT RES SHR SWAP DATA内存参数详解
  18. Java子类访问父类私有变量的思考
  19. 复频域求零输入响应_信号处理的复频域分析方法推荐
  20. asp.net酒店餐饮管理系统

热门文章

  1. CSS 文字下划线间距
  2. CSS让同一行的图片和文字垂直居中对齐
  3. Adobe XD常见问题和解决方案
  4. 2010年小学生学习全能托管
  5. snmp v3 参数_snmp v3配置使用
  6. 班章管家:理财产品哪个好一些?从以下几个方面比较
  7. 企业与个人是如何跨越战略转折点 - 读《只有偏执狂才能生存》
  8. python 桑基图_流量结构分布图——桑基图(Sankey)
  9. 第二十一章 数据库角色
  10. java jdk 1.8中lambda表达式常用方法