IjkPlayer 学习笔记

笔记可能微乱,但大致清晰,可能会对他人有所帮助,故分享出来。

×××××××××××××××××××目录×××××××××××××××××××
ijk概述
mediacodec相关
OpenGL相关
filter相关
setOption配置相关
metadata相关
h264编码器特有的设置域
线程相关
消息机制
音频输出
声道切换
SDL_CreateCond 与 SDL_CreateThreadEx
如何暂停
×××××××××××××××××××××××××××××××××××××××××


ijk概述


《零基础读懂视频播放器控制原理: ffplay 播放器源代码分析》: https://cloud.tencent.com/developer/article/1004559
《基于 ffmpeg 的跨平台播放器实现》: https://cloud.tencent.com/developer/article/1004561
《ijkplayer框架深入剖析》: https://www.jianshu.com/p/daf0a61cc1e0

三种播放器实现: 均继承自 AbstractMediaPlayer 继承自 IMediaPlayer
1.AndroidMediaPlayer:基于安卓自带播放器(位于ijkplayer-java)
2.IjkExoMediaPlayer:基于ExoPlayer(位于ijkplayer-exo)
介绍:http://blog.csdn.net/u014606081/article/details/76181049
3.IjkMediaPlayer:基于ffplay(位于ijkplayer-java,底层实现在ijkmedia目录)
输出:
video-output: NativeWindow, OpenGL ES 2.0
audio-output: AudioTrack, OpenSL ES
jni底层接口:
IjkMediaPlayer : ijkmedia/ijkplayer/android/ijkplayer_jni.c
播放器结构体:VideoState(ff_ffplay_def.c )
播放入口:
ffplay.c : ffp_prepare_async_l
stream_open :创建音视频解码前后队列, 创建数据读取(read_thread)和视频显示线程(video_refresh_thread)
1.read_thread:读取packet
stream_component_open: 打开视频、音频解码器。在此会打开相应解码器,并创建相应的解码线程
av_read_frame:读取媒体数据,得到的是音视频分离的解码前数据
packet_queue_put:往缓冲队列中放入解码前的音、视、字幕 packet
打开视频解码器:
ffplay.c : stream_component_open
ffpipeline_open_video_decoder
ff_ffpipeline.c : ffpipeline_open_video_decoder 调用 IJKFF_Pipeline->func_open_video_decoder
IJKFF_Pipeline->func_open_video_decoder 函数指针指向 android/pipeline/cffpipeline_android.c 的 func_open_video_decoder 方法
然后调用 android/pipeline/ffpipenode_android_mediacodec_vdec.c 的 ffpipenode_create_video_decoder_from_android_mediacodec
2.video_thread:解码
ff_ffpipenode.c : ffpipenode_run_sync
/android/pipeline/ffpipenode_android_mediacodec_vdec.c : func_run_sync 解码后的帧加入帧缓冲队列
IJKFF_Pipenode IJKFF_Pipenode_Opaque
视频显示:
video_refresh_thread
video_refresh
frame_queue_peek_last
video_display2
video_image_display2
ijksdl/ijksdl_vout.c : SDL_VoutDisplayYUVOverlay
ijksdl_vout_android_nativewindow.c vout->display_overlay = func_display_overlay
func_display_overlay
func_display_overlay_l
音视频同步:
video_refresh


mediacodec相关


1.为什么ijk硬解不用ffmpeg自带的mediacodec_wrapper,而是自己在底层封装的java api
https://github.com/Bilibili/ijkplayer/issues/1705
https://github.com/Bilibili/ijkplayer/issues/1557
FFmpeg3.1中也集成了MediaCodec硬件解码
ijkplayer doesn't use ffmpeg's mediacodec implement
ijkplayer has its own mediacodec implement.


OpenGL相关


1.Android MediaCodec and OpenGL Render
https://github.com/Bilibili/ijkplayer/issues/2893
2.ijk android是支持opengl的
https://github.com/Bilibili/ijkplayer/issues/338

ijksdl/gles2/fsh/ 下脚本可修改gl渲染效果

问题:
播放时软解解码出的为yuv420p格式,可通过gl渲染
播放时硬解解码出不能通过gl渲染,视频帧格式为 QCOM_FORMATYUV420PackedSemiPlanar32m
分析:
不同设备硬解出的视频帧格式不同:
https://www.polarxiong.com/archives/Android-MediaCodec%E8%A7%86%E9%A2%91%E6%96%87%E4%BB%B6%E7%A1%AC%E4%BB%B6%E8%A7%A3%E7%A0%81-%E9%AB%98%E6%95%88%E7%8E%87%E5%BE%97%E5%88%B0YUV%E6%A0%BC%E5%BC%8F%E5%B8%A7-%E5%BF%AB%E9%80%9F%E4%BF%9D%E5%AD%98JPEG%E5%9B%BE%E7%89%87-%E4%B8%8D%E4%BD%BF%E7%94%A8OpenGL.html
比如红米设备上硬解出的帧格式为:YUV420PackedSemiPlanar32m
ijk中的gl渲染只支持yuv420、rgb等,需要把 YUV420PackedSemiPlanar32m 转为 YUV 才可以
转换算法:http://blog.csdn.net/u011270282/article/details/50698243
解决:
播放时的解码速度并不严格要求,先用软解,软解解出的为yuv420p,可以直接gl渲染
遗留优化项:
若采用软解播放则进行内存比较,若硬解较优,则硬解后将视频帧转换为gl可以渲染的格式,当然也要考虑转换的时间消耗


filter相关


1.Does ijkplayer support video filters , ijk支持java层直接设置滤镜!
https://github.com/Bilibili/ijkplayer/issues/3686


setOption配置相关


mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0);
"mediacodec"等配置项位于:ff_ffplayer_options.h 以及 option_table.h
如何利用 AVDictionary 配置参数 : http://blog.csdn.net/encoder1234/article/details/54582676

AVClass 与 AVOption : http://blog.csdn.net/leixiaohua1020/article/details/44268323
AVClass就是AVOption和目标结构体之间的“桥梁”。
AVClass中存储了AVOption类型的数组option,用于存储选项信息。
AVClass有一个特点就是它必须位于其支持的结构体的第一个位置


metadata相关


对于metadata的操作封装于 ijkmeta.c , metadata信息声明在ijkmeta.h中, java层对应IjkMediaMeta.java
IjkMediaMeta分两层,外层metadata,内层:video、audio、subtitle三个metadata,共4个metadata
显然 ijkmeta.c 中只针对播放流程,也就是只负责从视频中获取 metadata ,然后在播放时获取 metadata 信息
保存时应该将metadata信息写入视频,首先要搞清楚需要写入哪些metadata信息?
应该是:凡是播放时需要获取的都需要写入,这样才能正常播放
How to set header metadata to encoded video?
https://stackoverflow.com/questions/17024192/how-to-set-header-metadata-to-encoded-video
avformat.h有对metadata的用法介绍


h264编码器特有的设置域


具体见:libx264.c options

  /* priv_data 属于每个编码器特有的设置域,用 av_opt_set 设置  */
  /**
   *  preset : 编码模式
   * ultrafast,superfast, veryfast, faster, fast, medium, slow, slower, veryslow
   * fast 节省约 10% encoding time   10s视频100s
   * faster 25%
   * ultrafast 55% 10s视频16s
   * 但越快质量越低
   */

  av_opt_set(enc_ctx->priv_data, "preset", "ultrafast", 0);

  /**
   * lookahead:编码码率控制所需要锁定的帧个数
   */
  av_opt_set(enc_ctx->priv_data, "lookahead", "0", 0);

  /**
   * 使用2pass编码模式
   * 1pass和2pass的区别在于1pass只需要编码一次,
   * 2pass需要编码两次。2pass的优点在于可编码更小的文件,缺点在于所花费时间比1pass更多
   */
  av_opt_set(enc_ctx->priv_data, "2pass", "0", 0);

  /**
   * 无延时输出
   */
  av_opt_set(enc_ctx->priv_data, "zerolatency", "1", 0);


线程相关


ijk线程相关操作位于ijksdl_thread.c,封装了pthread库
以解码为例:
d->decoder_tid = SDL_CreateThreadEx(&d->_decoder_tid, fn, arg, name);
在 SDL_CreateThreadEx 方法其实是调用 pthread_create 方法
ijk 封装了线程的初始化和销毁操作,见 ijksdl_thread.c 的 SDL_RunThread方法


消息机制


native_setup 时设置循环读消息函数, mp->msg_loop = msg_loop ,初始化消息队列在 FFPlayer 中
在 ijkmp_prepare_async_l 时创建线程运行循环读消息函数


音频输出


新建播放器实例(native_setup)时,根据ffp->opensles 初始化 opensles 或 androidTrack 输出设备
在 opensles 或 androidTrack 中,通过回调 sdl_audio_callback 方法从音频帧缓冲队列中读取音频数据,播放之
比如在 ijksdl_aout_android_opensles.c 中 audio_cblk 就是 sdl_audio_callback 方法


声道切换


ijkplayer如何切换音轨,以及获取音轨信息:https://github.com/Bilibili/ijkplayer/issues/3811
IjkMediaPlayer.java 跟轨道相关的方法 :
getTrackInfo(获取所有轨道)、getSelectedTrack(获取当前轨道)、selectTrack(选择轨道)
selectTrack(选择轨道)可实现切换轨道操作
对应调用 ff_ffplayer.c 的 ffp_set_stream_selected 方法
此方法中通过调用 stream_component_close、stream_component_open 实现轨道切换


SDL_CreateCond 与 SDL_CreateThreadEx


ff_ffplay.c 中有一句代码: is->continue_read_thread = SDL_CreateCond()
乍一看变量名会以为 SDL_CreateCond 方法是用来创建线程... 不对! ,SDL_CreateThreadEx 才是用来创建线程的
SDL_CreateCond 方法调用了 pthread_cond_init 方法,含义为:创建条件变量
条件变量相关:
1.初始化条件变量 pthread_cond_init
2.阻塞在条件变量上pthread_cond_wait
3.解除在条件变量上的阻塞pthread_cond_signal
4.阻塞直到指定时间pthread_cond_timedwait
5.释放阻塞的所有线程pthread_cond_broadcast
6.释放条件变量pthread_cond_destroy
详细:https://blog.csdn.net/ithomer/article/details/6031723
ijksdl_mutex.c 中封装了互斥锁相关操作


如何暂停


IjkMediaPlayer.java 中的 _pause 本地方法调用 ijkplayer_jni.c 中的 IjkMediaPlayer_pause
然后通过ijk的消息机制发送暂停信号: ffp_notify_msg1(mp->ffplayer, FFP_REQ_PAUSE);
然后调用到 ff_ffplay.c 的 ffp_pause_l 方法,最后调用到 stream_toggle_pause_l 方法
其中两个关键操作:1.is->paused 置 true ; 2.调用 SDL_AoutPauseAudio 方法
其实在读包、解码、渲染/播放这个工作线中,只要有一处停止工作,其他地方自然会阻塞住即可,
但是看代码发现多处都有根据is->paused来停止工作的逻辑,列一下自认为比较关键的地方:
对于视频:在视频渲染线程的 video_refresh 方法中:
if (is->paused) goto display;
如果暂停了,就一直显示当前帧,跳过后面的取下一帧操作,不取下一帧,帧队列满后自然就会停止解码、读包操作。
对于音频: 调用 SDL_AoutPauseAudio 方法后不再回调 sdl_audio_callback 方法(参见上面的《音频输出》)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容