bitmap类型字幕多见于蓝光片源。但是在原生ijkplayer中,只有针对文本类型字幕的处理,而不支持bitmap类型字幕,相关代码如下

//static void video_image_display2(FFPlayer *ffp) @ ff_ffplay.c
if (is->subtitle_st) {if (frame_queue_nb_remaining(&is->subpq) > 0) {sp = frame_queue_peek(&is->subpq);if (vp->pts >= sp->pts + ((float) sp->sub.start_display_time / 1000)) {if (!sp->uploaded) {if (sp->sub.num_rects > 0) {char buffered_text[4096];if (sp->sub.rects[0]->text) {   //在这里只对text类型和ass类型的字幕做了相应的的处理strncpy(buffered_text, sp->sub.rects[0]->text, 4096);}else if (sp->sub.rects[0]->ass) {parse_ass_subtitle(sp->sub.rects[0]->ass, buffered_text);}ffp_notify_msg4(ffp, FFP_MSG_TIMED_TEXT, 0, 0, buffered_text, sizeof(buffered_text));}sp->uploaded = 1;}}}}

当sp->sub.format==0时,对应的即为bitmap类型的字幕格式,如下定义

typedef struct AVSubtitle {uint16_t format; /* 0 = graphics */uint32_t start_display_time; /* relative to packet pts, in ms */uint32_t end_display_time; /* relative to packet pts, in ms */unsigned num_rects;AVSubtitleRect **rects;int64_t pts;    ///< Same as packet pts, in AV_TIME_BASE
} AVSubtitle;

因此,要让ijkplayer支持bitmap类型字幕,需要做以下几件事情:

1.在video_image_display2方法中根据sp→sub.format的值区分字幕类型,当是bitmap类型字幕时,取出相应的bmp像素数据

2.在jni层(ijkplayer_jni.c),利用拿到的bmp像素数据,调用android.graphics.Bitmap类中的方法,创建出相应的Bitmap对象

3.在java层(IjkMediaPlayer.java),拿到Bitmap对象,调用android.graphic.Canvas类的drawBitmap方法,绘制到单独的View中,从而显示出字幕的内容


下面来具体看一下每一步相关的代码

1.native层的修改,以PGS格式字幕为例

if (is->subtitle_st) {if (frame_queue_nb_remaining(&is->subpq) > 0) {sp = frame_queue_peek(&is->subpq);if (vp->pts >= sp->pts + ((float) sp->sub.start_display_time / 1000)) {if (!sp->uploaded) {if (sp->sub.num_rects > 0) {if (sp->sub.format != 0) { //此时字幕类型为文本类型,还是按照原有逻辑处理char buffered_text[4096];if (sp->sub.rects[0]->text) {strncpy(buffered_text, sp->sub.rects[0]->text, 4096);}else if (sp->sub.rects[0]->ass) {parse_ass_subtitle(sp->sub.rects[0]->ass, buffered_text);}ffp_notify_msg4(ffp, FFP_MSG_TIMED_TEXT, 0, 0, buffered_text, sizeof(buffered_text));} else { //此时字幕类型为bitmap类型if (sp->sub.rects[0]) {if (sp->sub.rects[0]->data[0] && sp->sub.rects[0]->linesize[0] > 0) {//w和h记录了bitmap字幕的宽和高。sp->sub.rects[0]还有成员x和y,记录的是bitmap字幕的坐标,因为我们想要的是可以调整字幕位置,所以不需要这两个变量。int len = sp->sub.rects[0]->w * sp->sub.rects[0]->h;                                    //每个像素数据包含rgba四个字节uint8_t buffered_img[len*4];for(int i = 0; i < len; i++) {//因为是bitmap类型,所以像素数据在sp->sub.rects[0]中分两块存储,data[1]中保存的是色表(palette),data[0]中保存的色表索引值//从data[0]中拿到色表索引int coloridx = sp->sub.rects[0]->data[0][i] * 4;//到data[1]中拿到对应的颜色值buffered_img[i*4] = sp->sub.rects[0]->data[1][coloridx];//Rbuffered_img[i*4 + 1] = sp->sub.rects[0]->data[1][coloridx + 1];//Gbuffered_img[i*4 + 2] = sp->sub.rects[0]->data[1][coloridx + 2];//Bbuffered_img[i*4 + 3] = sp->sub.rects[0]->data[1][coloridx + 3];//A}//自定义一个新的消息类型FFP_MSG_BITMAP_SUBTITLE,用于传递像素数据ffp_notify_msg4(ffp, FFP_MSG_BITMAP_SUBTITLE, sp->sub.rects[0]->w, sp->sub.rects[0]->h, buffered_img, len*4);}}}}sp->uploaded = 1;}}}}

2.jni层的修改,在message_loop_n方法中,添加

case FFP_MSG_BITMAP_SUBTITLE:if (msg.obj) {int _width = msg.arg1;int _height = msg.arg2;uint8_t* bitmap = (uint8_t*) msg.obj;jclass bitmapConfig = (*env)->FindClass(env, "android/graphics/Bitmap$Config");jfieldID rgba8888FieldID = (*env)->GetStaticFieldID(env, bitmapConfig, "ARGB_8888", "Landroid/graphics/Bitmap$Config;");jobject rgba8888Obj = (*env)->GetStaticObjectField(env, bitmapConfig, rgba8888FieldID);jclass bitmapClass = (*env)->FindClass(env, "android/graphics/Bitmap");jmethodID createBitmapMethodID = (*env)->GetStaticMethodID(env, bitmapClass,"createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");jobject bitmapObj = (*env)->CallStaticObjectMethod(env, bitmapClass, createBitmapMethodID, _width, _height, rgba8888Obj);jintArray pixels = (*env)->NewIntArray(env, _width * _height);for (int i = 0; i < _width * _height; i++){unsigned char red = bitmap[i*4];unsigned char green = bitmap[i*4 + 1];unsigned char blue = bitmap[i*4 + 2];unsigned char alpha = bitmap[i*4 + 3];int currentPixel = (alpha << 24) | (red << 16) | (green << 8) | (blue);(*env)->SetIntArrayRegion(env, pixels, i, 1, &currentPixel);}jmethodID setPixelsMid = (*env)->GetMethodID(env, bitmapClass, "setPixels", "([IIIIIII)V");(*env)->CallVoidMethod(env, bitmapObj, setPixelsMid, pixels, 0, _width, 0, 0, _width, _height);post_event2(env, weak_thiz, MEDIA_BITMAP_SUBTITLE, msg.arg1, msg.arg2, bitmapObj);//自定义MEDIA_BITMAP_SUBTITLE消息,用于传递Bitmap对象J4A_DeleteLocalRef__p(env, &bitmapObj);} else {post_event2(env, weak_thiz, MEDIA_BITMAP_SUBTITLE, 0, 0, NULL);}break;

3.java层的修改,在IjkMediaPlayer.java的EventHandler中,添加

case MEDIA_BITMAP_SUBTITLE:if (msg.obj == null) {player.notifyOnTimedText(null);} else {int bitmapWidth = msg.arg1;int bitmapHeight = msg.arg2;//拿到jni层传递过来的Bitmap对象Bitmap bitmap = (Bitmap) msg.obj;// Build the cue.int planeWidth = player.mVideoWidth;int planeHeight = player.mVideoHeight;int bitmapX = (planeWidth - bitmapWidth) / 2;//默认在水平居中,靠近画面底部的位置显示字幕,所以可以简单计算出一个坐标值int bitmapY = planeHeight - bitmapHeight - 50;//创建字幕对象,稍后讲解IjkSubtitle subtitle = new IjkSubtitle(bitmap,(float) bitmapX / planeWidth,IjkSubtitle.ANCHOR_TYPE_START,(float) bitmapY / planeHeight,IjkSubtitle.ANCHOR_TYPE_START,(float) bitmapWidth / planeWidth,(float) bitmapHeight / planeHeight);//通知到IjkVideoView中,进行显示player.notifyOnTimedText(subtitle);}return;

在上面用到了一个IjkSubtitle类,这个类移植自ExoPlayer的com/google/android/exoplayer2/text/Cue.java。在ExoPlayer中有一套完整的图片与文字字幕渲染、样式修改的工具类,所以可以很方便的移植过来使用,包含以下几个类

com/google/android/exoplayer2/text/CaptionStyleCompat.java

com/google/android/exoplayer2/ui/SubtitleView.java

com/google/android/exoplayer2/ui/SubtitlePainter.java

由此,即完成了图片字幕的显示

关注公众号,掌握更多多媒体领域知识与资讯

文章帮到你了?可以扫描如下二维码进行打赏,打赏多少您随意~

ijkplayer实现图形字幕的播放相关推荐

  1. 基于ijkplayer封装的UE4安卓播放器插件

    基于ijkplayer封装的UE4安卓播放器插件 关于 ijkplayerUE4 相关的 Github 地址 使用方法 运行后的demo 关于 ijkplayerUE4 基于bilibili开源项目 ...

  2. 如何使用Premiere传统字幕于图形字幕

    传统字幕于图形字幕图文教程 1.启动 Premiere:软件,新建一个项目. 2.新建一个素材箱,把我们准备的图片素材和音频素材导入到素材箱中并把图片拖到时间轴上.如图所示 3.在"新建字幕 ...

  3. Ae508 4000预设素材包ae与pr脚本一键式安装背景图形字幕动画转场动态海报广告视频aepr脚本模板

    Ae508 4000预设素材包ae与pr脚本一键式安装背景图形字幕动画转场动态海报广告视频aepr脚本模板 [脚本要求] 使用帮助:中文安装说明(win/Mac提供一键快速安装)英文使用视频 脚本版本 ...

  4. GSYVideoPlayer(基于ijkplayer)之rtmp协议播放器的简单应用

    基于ijkplayer的GSYVideoPlayer rtmp协议播放器的简单应用 **添加依赖** **Manifest配置** **Layout布局** activity_start.xml ac ...

  5. 开源播放器 ijkplayer (四) :Ijkplayer切换网络时停止播放的问题处理

    问题起因: 在进行ijkplayer播放器的测试时,发现ijkplayer播放器在切换网络时出现直播画面停止的问题. 问题分析: 抓取日志发现:tv.danmaku.ijk.media.player. ...

  6. 基于ijkplayer实现低延迟直播播放器

    文章目录 前言 rtmp产生延迟的原因 如何减少播放器播放延迟 追帧策略定义和工程实现细节 直播播放器追帧策略 基于ijkplayer[^1]的工程实现 总结 推荐免费直播学习课程: [c/c++Li ...

  7. 画出android音乐播放器的类图,基于Qt图形框架音乐播放器的设计与实现

    随着互联网软件行业的快速发展,应用市场的软件种类更是琳琅满目,其中,生活娱乐类的软件种类最为繁多,就以本文涉及到的音乐播放器(多媒体)来说,国内目前的主流音乐播放器有QQ音乐.酷狗音乐.百度音乐.网易 ...

  8. Android开发-基于ijkplayer框架开发网络电视直播播放器的实现

    https://blog.csdn.net/fukaimei/article/details/80553709 前 言 ijkplayer框架是由B站在GitHub开源的一款比较好用的开源网络播放器框 ...

  9. 可以自动下载字幕的播放器-shooter player 射手播放器

    <script type="text/javascript"></script> <script type="text/javascript ...

最新文章

  1. 刘宇与小白健康:一个理想主义者的互联网“众包”实践
  2. 【译】Understanding Universal Composition Framework and Sprites State Channels
  3. JAVA 串口编程 (三)
  4. 天平游码读数例题_初二上册物理实验——托盘天平使用的注意事项
  5. 用Java递增Map值的最有效方法–仅搜索一次键
  6. linux 命令案例学习——文件搜索
  7. 前端学习(1869)vue之电商管理系统电商系统之配置axios发出登录请求
  8. 安卓9 webview打开指定url报错或者空白
  9. c语言如何用数组对字符串排序,怎么用qsort对二维字符数组存的若干字符串排序...
  10. 鬼话描绘形式_桥接形式
  11. 网站做渗透测试服务的步骤
  12. 谷歌浏览器flash被禁用解决方法
  13. nextpolish安装_Polish安装问题
  14. 详解Win10系统下打开.jks签名文件的方法
  15. 问题解决:consider to specify kernel configuration cache directory through OPENCV_OCL4DNN_CONFIG_PATH par
  16. SAP软件ERP系统简介
  17. Jenkins使用过程遇到的问题记录
  18. Codevs 1228 苹果树
  19. Linux chown命令学习
  20. linux 如何注册服务,Linux 系统服务注册

热门文章

  1. VS2013 0x80070643报错解决方案 KB2829760
  2. [XCTF]funny_video(难度2)
  3. 在 Visual Studio 2010 中创建 ASP.NET Web 项目
  4. 企业开源治理案例及开源项目列表
  5. FPGA学习日志——OV7670寄存器配置
  6. 【笔记】python的遍历字典:遍历所有键值对(方法items())、遍历字典中的所有键(方法keys())、按特定顺序遍历字典中的所有键、遍历字典中的所有值(方法values())、函数set()
  7. android开发 wifi功率,一种基于Android系统的功耗计算方法与流程
  8. mac在终端中使用vscode打开文件
  9. 2014全国计算机等级考试二级java,全国计算机等级考试二级Java真题2
  10. 手机端在线商城开发(前端vue+后台php)