1 Star 1 Fork 0

冰奇/QHAIDanmuMan

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
IJKPlayer-AIDanmu.patch 82.69 KB
一键复制 编辑 原始数据 按行查看 历史
AnakinChen 提交于 2021-12-31 00:55 . 【Init】文章 + Demo
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336
From 438ce2d0705bd865c428252883c1c8c31981f036 Mon Sep 17 00:00:00 2001
From: AnakinChen <qihuichen@sohu-inc.com>
Date: Mon, 6 Dec 2021 01:06:04 +0800
Subject: [PATCH] =?UTF-8?q?=E3=80=90=E6=99=BA=E8=83=BD=E5=BC=B9=E5=B9=95?=
=?UTF-8?q?=E3=80=91=E3=80=90Add=E3=80=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
ijkmedia/ijkplayer/ff_ffplay.c | 2 +-
ijkmedia/ijksdl/gles2/fsh/yuv420p.fsh.c | 58 ++
ijkmedia/ijksdl/gles2/internal.h | 2 +
ijkmedia/ijksdl/gles2/renderer.c | 9 +-
ijkmedia/ijksdl/gles2/renderer_yuv420p.c | 8 +-
ijkmedia/ijksdl/ijksdl_gles2.h | 1 +
.../IJKMediaDemo.xcodeproj/project.pbxproj | 61 ++
.../IJKMoviePlayerViewController.m | 70 +++
.../QHDanmuMan/Core/QHDanmuManager.h | 51 ++
.../QHDanmuMan/Core/QHDanmuManager.m | 143 +++++
.../QHDanmuMan/Core/QHDanmuView.h | 97 +++
.../QHDanmuMan/Core/QHDanmuView.m | 501 ++++++++++++++++
.../QHDanmuMan/Core/QHDanmuViewCell.h | 45 ++
.../QHDanmuMan/Core/QHDanmuViewCell.m | 71 +++
.../QHUtilMan/NSTimer+QHEOCBlocksSupport.h | 26 +
.../QHUtilMan/NSTimer+QHEOCBlocksSupport.m | 24 +
.../IJKMediaDemo/QHUtilMan/QHBaseUtil.h | 23 +
.../IJKMediaDemo/QHUtilMan/QHBaseUtil.m | 31 +
.../IJKMediaDemo/QHUtilMan/QHViewUtil.h | 25 +
.../IJKMediaDemo/QHUtilMan/QHViewUtil.m | 41 ++
.../IJKFFMoviePlayerController.h | 2 +
.../IJKFFMoviePlayerController.m | 4 +
.../ijkmedia/ijksdl/ios/IJKSDLGLView.h | 20 +-
.../ijkmedia/ijksdl/ios/IJKSDLGLView.m | 554 +++++++++++-------
ios/compile-ffmpeg.sh | 6 +-
25 files changed, 1638 insertions(+), 237 deletions(-)
create mode 100644 ios/IJKMediaDemo/IJKMediaDemo/QHDanmuMan/Core/QHDanmuManager.h
create mode 100644 ios/IJKMediaDemo/IJKMediaDemo/QHDanmuMan/Core/QHDanmuManager.m
create mode 100644 ios/IJKMediaDemo/IJKMediaDemo/QHDanmuMan/Core/QHDanmuView.h
create mode 100644 ios/IJKMediaDemo/IJKMediaDemo/QHDanmuMan/Core/QHDanmuView.m
create mode 100644 ios/IJKMediaDemo/IJKMediaDemo/QHDanmuMan/Core/QHDanmuViewCell.h
create mode 100644 ios/IJKMediaDemo/IJKMediaDemo/QHDanmuMan/Core/QHDanmuViewCell.m
create mode 100644 ios/IJKMediaDemo/IJKMediaDemo/QHUtilMan/NSTimer+QHEOCBlocksSupport.h
create mode 100644 ios/IJKMediaDemo/IJKMediaDemo/QHUtilMan/NSTimer+QHEOCBlocksSupport.m
create mode 100644 ios/IJKMediaDemo/IJKMediaDemo/QHUtilMan/QHBaseUtil.h
create mode 100644 ios/IJKMediaDemo/IJKMediaDemo/QHUtilMan/QHBaseUtil.m
create mode 100644 ios/IJKMediaDemo/IJKMediaDemo/QHUtilMan/QHViewUtil.h
create mode 100644 ios/IJKMediaDemo/IJKMediaDemo/QHUtilMan/QHViewUtil.m
diff --git a/ijkmedia/ijkplayer/ff_ffplay.c b/ijkmedia/ijkplayer/ff_ffplay.c
index 8371cf18..524fc3e7 100755
--- a/ijkmedia/ijkplayer/ff_ffplay.c
+++ b/ijkmedia/ijkplayer/ff_ffplay.c
@@ -3649,7 +3649,7 @@ static int video_refresh_thread(void *arg)
VideoState *is = ffp->is;
double remaining_time = 0.0;
while (!is->abort_request) {
- printf("waiting->%f\n", remaining_time);
+// printf("waiting->%f\n", remaining_time);
if (remaining_time > 0.0)
av_usleep((int)(int64_t)(remaining_time * 1000000.0));
remaining_time = REFRESH_RATE;
diff --git a/ijkmedia/ijksdl/gles2/fsh/yuv420p.fsh.c b/ijkmedia/ijksdl/gles2/fsh/yuv420p.fsh.c
index 39206c07..0fcfbcb6 100644
--- a/ijkmedia/ijksdl/gles2/fsh/yuv420p.fsh.c
+++ b/ijkmedia/ijksdl/gles2/fsh/yuv420p.fsh.c
@@ -42,7 +42,65 @@ static const char g_shader[] = IJK_GLES_STRING(
}
);
+static const char g_shader_front[] = IJK_GLES_STRING(
+ precision highp float;
+ varying highp vec2 vv2_Texcoord;
+ uniform mat3 um3_ColorConversion;
+ uniform lowp sampler2D us2_SamplerX;
+ uniform lowp sampler2D us2_SamplerY;
+ uniform lowp sampler2D us2_SamplerZ;
+
+// uniform mediump float v_mesh[8];
+
+ void main()
+ {
+ mediump float fx = vv2_Texcoord.x;
+ mediump float fy = vv2_Texcoord.y;
+
+ mediump float x[4];
+ mediump float y[4];
+
+ x[0] = 0.3;
+ x[1] = 0.6;
+ x[3] = 0.3;
+ x[2] = 0.6;
+
+ y[0] = 0.2;
+ y[1] = 0.2;
+ y[3] = 0.6;
+ y[2] = 0.6;
+
+ mediump float a;
+ mediump float b;
+ mediump float c;
+ mediump float d;//分别存四个向量的计算结果;
+ a = (x[1] - x[0])*(fy - y[0]) - (y[1] - y[0])*(fx - x[0]);
+ b = (x[2] - x[1])*(fy - y[1]) - (y[2] - y[1])*(fx - x[1]);
+ c = (x[3] - x[2])*(fy - y[2]) - (y[3] - y[2])*(fx - x[2]);
+ d = (x[0] - x[3])*(fy - y[3]) - (y[0] - y[3])*(fx - x[3]);
+ if ((a >= 0.0 && b >= 0.0 && c >= 0.0 && d >= 0.0) || (a <= 0.0 && b <= 0.0 && c <= 0.0 && d <= 0.0)) {
+ mediump vec3 yuv;
+ lowp vec3 rgb;
+
+ yuv.x = (texture2D(us2_SamplerX, vv2_Texcoord).r - (16.0 / 255.0));
+ yuv.y = (texture2D(us2_SamplerY, vv2_Texcoord).r - 0.5);
+ yuv.z = (texture2D(us2_SamplerZ, vv2_Texcoord).r - 0.5);
+
+ rgb = um3_ColorConversion * yuv;
+ gl_FragColor = vec4(rgb, 1);
+ }
+ else {
+ gl_FragColor = vec4(1, 1, 1, 0);
+ }
+ }
+);
+
const char *IJK_GLES2_getFragmentShader_yuv420p()
{
return g_shader;
}
+
+const char *IJK_GLES2_getFragmentShader_yuv420p_4Front()
+{
+ return g_shader_front;
+}
diff --git a/ijkmedia/ijksdl/gles2/internal.h b/ijkmedia/ijksdl/gles2/internal.h
index edafc1da..f07f2c3d 100644
--- a/ijkmedia/ijksdl/gles2/internal.h
+++ b/ijkmedia/ijksdl/gles2/internal.h
@@ -85,6 +85,7 @@ void IJK_GLES2_loadOrtho(IJK_GLES_Matrix *matrix, GLfloat left, GLfloat right, G
const char *IJK_GLES2_getVertexShader_default();
const char *IJK_GLES2_getFragmentShader_yuv420p();
+const char *IJK_GLES2_getFragmentShader_yuv420p_4Front();
const char *IJK_GLES2_getFragmentShader_yuv444p10le();
const char *IJK_GLES2_getFragmentShader_yuv420sp();
const char *IJK_GLES2_getFragmentShader_rgb();
@@ -94,6 +95,7 @@ const GLfloat *IJK_GLES2_getColorMatrix_bt601();
IJK_GLES2_Renderer *IJK_GLES2_Renderer_create_base(const char *fragment_shader_source);
IJK_GLES2_Renderer *IJK_GLES2_Renderer_create_yuv420p();
+IJK_GLES2_Renderer *QH_IJK_GLES2_Renderer_create_yuv420p(bool bFront);
IJK_GLES2_Renderer *IJK_GLES2_Renderer_create_yuv444p10le();
IJK_GLES2_Renderer *IJK_GLES2_Renderer_create_yuv420sp();
IJK_GLES2_Renderer *IJK_GLES2_Renderer_create_yuv420sp_vtb(SDL_VoutOverlay *overlay);
diff --git a/ijkmedia/ijksdl/gles2/renderer.c b/ijkmedia/ijksdl/gles2/renderer.c
index 76c61273..e64b19bc 100644
--- a/ijkmedia/ijksdl/gles2/renderer.c
+++ b/ijkmedia/ijksdl/gles2/renderer.c
@@ -151,8 +151,13 @@ fail:
return NULL;
}
-
IJK_GLES2_Renderer *IJK_GLES2_Renderer_create(SDL_VoutOverlay *overlay)
+{
+ return QH_IJK_GLES2_Renderer_create_for(overlay, false);
+}
+
+
+IJK_GLES2_Renderer *QH_IJK_GLES2_Renderer_create_for(SDL_VoutOverlay *overlay, bool bFront)
{
if (!overlay)
return NULL;
@@ -172,7 +177,7 @@ IJK_GLES2_Renderer *IJK_GLES2_Renderer_create(SDL_VoutOverlay *overlay)
case SDL_FCC__VTB: renderer = IJK_GLES2_Renderer_create_yuv420sp_vtb(overlay); break;
#endif
case SDL_FCC_YV12: renderer = IJK_GLES2_Renderer_create_yuv420p(); break;
- case SDL_FCC_I420: renderer = IJK_GLES2_Renderer_create_yuv420p(); break;
+ case SDL_FCC_I420: renderer = QH_IJK_GLES2_Renderer_create_yuv420p(bFront); break;
case SDL_FCC_I444P10LE: renderer = IJK_GLES2_Renderer_create_yuv444p10le(); break;
default:
ALOGE("[GLES2] unknown format %4s(%d)\n", (char *)&overlay->format, overlay->format);
diff --git a/ijkmedia/ijksdl/gles2/renderer_yuv420p.c b/ijkmedia/ijksdl/gles2/renderer_yuv420p.c
index 1bad7388..b5e86f2d 100644
--- a/ijkmedia/ijksdl/gles2/renderer_yuv420p.c
+++ b/ijkmedia/ijksdl/gles2/renderer_yuv420p.c
@@ -97,10 +97,14 @@ static GLboolean yuv420p_uploadTexture(IJK_GLES2_Renderer *renderer, SDL_VoutOve
return GL_TRUE;
}
-IJK_GLES2_Renderer *IJK_GLES2_Renderer_create_yuv420p()
+IJK_GLES2_Renderer *IJK_GLES2_Renderer_create_yuv420p() {
+ return QH_IJK_GLES2_Renderer_create_yuv420p(false);
+}
+
+IJK_GLES2_Renderer *QH_IJK_GLES2_Renderer_create_yuv420p(bool bFront)
{
ALOGI("create render yuv420p\n");
- IJK_GLES2_Renderer *renderer = IJK_GLES2_Renderer_create_base(IJK_GLES2_getFragmentShader_yuv420p());
+ IJK_GLES2_Renderer *renderer = IJK_GLES2_Renderer_create_base(bFront ? IJK_GLES2_getFragmentShader_yuv420p_4Front() : IJK_GLES2_getFragmentShader_yuv420p());
if (!renderer)
goto fail;
diff --git a/ijkmedia/ijksdl/ijksdl_gles2.h b/ijkmedia/ijksdl/ijksdl_gles2.h
index 7c1fdd51..c5a75d1b 100644
--- a/ijkmedia/ijksdl/ijksdl_gles2.h
+++ b/ijkmedia/ijksdl/ijksdl_gles2.h
@@ -58,6 +58,7 @@ GLuint IJK_GLES2_loadShader(GLenum shader_type, const char *shader_source);
typedef struct IJK_GLES2_Renderer IJK_GLES2_Renderer;
IJK_GLES2_Renderer *IJK_GLES2_Renderer_create(SDL_VoutOverlay *overlay);
+IJK_GLES2_Renderer *QH_IJK_GLES2_Renderer_create_for(SDL_VoutOverlay *overlay, bool bFront);
void IJK_GLES2_Renderer_reset(IJK_GLES2_Renderer *renderer);
void IJK_GLES2_Renderer_free(IJK_GLES2_Renderer *renderer);
void IJK_GLES2_Renderer_freeP(IJK_GLES2_Renderer **renderer);
diff --git a/ios/IJKMediaDemo/IJKMediaDemo.xcodeproj/project.pbxproj b/ios/IJKMediaDemo/IJKMediaDemo.xcodeproj/project.pbxproj
index 6f839b46..3c3ed0b7 100644
--- a/ios/IJKMediaDemo/IJKMediaDemo.xcodeproj/project.pbxproj
+++ b/ios/IJKMediaDemo/IJKMediaDemo.xcodeproj/project.pbxproj
@@ -20,6 +20,12 @@
55E809F31B146DE8003E98A5 /* IJKQRCodeScanViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 55E809F21B146DE8003E98A5 /* IJKQRCodeScanViewController.xib */; };
55E809F61B1480BC003E98A5 /* IJKDemoHistory.m in Sources */ = {isa = PBXBuildFile; fileRef = 55E809F51B1480BC003E98A5 /* IJKDemoHistory.m */; };
55E809F91B15A1DB003E98A5 /* IJKDemoLocalFolderViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 55E809F81B15A1DB003E98A5 /* IJKDemoLocalFolderViewController.m */; };
+ 74D6704F275D259300ABDEE3 /* QHDanmuViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 74D67040275D259300ABDEE3 /* QHDanmuViewCell.m */; };
+ 74D67050275D259300ABDEE3 /* QHDanmuView.m in Sources */ = {isa = PBXBuildFile; fileRef = 74D67041275D259300ABDEE3 /* QHDanmuView.m */; };
+ 74D67051275D259300ABDEE3 /* QHDanmuManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 74D67042275D259300ABDEE3 /* QHDanmuManager.m */; };
+ 74D67052275D259300ABDEE3 /* QHBaseUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 74D6704B275D259300ABDEE3 /* QHBaseUtil.m */; };
+ 74D67053275D259300ABDEE3 /* QHViewUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 74D6704C275D259300ABDEE3 /* QHViewUtil.m */; };
+ 74D67054275D259300ABDEE3 /* NSTimer+QHEOCBlocksSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 74D6704E275D259300ABDEE3 /* NSTimer+QHEOCBlocksSupport.m */; };
E60E8C2A19EF70BB005B5B6E /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E60E8C2919EF70BB005B5B6E /* CoreMedia.framework */; };
E612EAE517F7E0F800BEE660 /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E612EAE417F7E0F800BEE660 /* MediaPlayer.framework */; };
E6166C9C17EDA4A20006B956 /* IJKMediaDemo-Prefix.pch in Resources */ = {isa = PBXBuildFile; fileRef = E6166C9B17EDA4A20006B956 /* IJKMediaDemo-Prefix.pch */; };
@@ -100,6 +106,18 @@
55E809F51B1480BC003E98A5 /* IJKDemoHistory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJKDemoHistory.m; sourceTree = "<group>"; };
55E809F71B15A1DB003E98A5 /* IJKDemoLocalFolderViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJKDemoLocalFolderViewController.h; sourceTree = "<group>"; };
55E809F81B15A1DB003E98A5 /* IJKDemoLocalFolderViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJKDemoLocalFolderViewController.m; sourceTree = "<group>"; };
+ 74D67040275D259300ABDEE3 /* QHDanmuViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QHDanmuViewCell.m; sourceTree = "<group>"; };
+ 74D67041275D259300ABDEE3 /* QHDanmuView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QHDanmuView.m; sourceTree = "<group>"; };
+ 74D67042275D259300ABDEE3 /* QHDanmuManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QHDanmuManager.m; sourceTree = "<group>"; };
+ 74D67043275D259300ABDEE3 /* QHDanmuView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QHDanmuView.h; sourceTree = "<group>"; };
+ 74D67044275D259300ABDEE3 /* QHDanmuViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QHDanmuViewCell.h; sourceTree = "<group>"; };
+ 74D67045275D259300ABDEE3 /* QHDanmuManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QHDanmuManager.h; sourceTree = "<group>"; };
+ 74D67049275D259300ABDEE3 /* QHViewUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QHViewUtil.h; sourceTree = "<group>"; };
+ 74D6704A275D259300ABDEE3 /* NSTimer+QHEOCBlocksSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSTimer+QHEOCBlocksSupport.h"; sourceTree = "<group>"; };
+ 74D6704B275D259300ABDEE3 /* QHBaseUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QHBaseUtil.m; sourceTree = "<group>"; };
+ 74D6704C275D259300ABDEE3 /* QHViewUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QHViewUtil.m; sourceTree = "<group>"; };
+ 74D6704D275D259300ABDEE3 /* QHBaseUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QHBaseUtil.h; sourceTree = "<group>"; };
+ 74D6704E275D259300ABDEE3 /* NSTimer+QHEOCBlocksSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTimer+QHEOCBlocksSupport.m"; sourceTree = "<group>"; };
E60E8C2919EF70BB005B5B6E /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; };
E612EAE417F7E0F800BEE660 /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; };
E6166C9B17EDA4A20006B956 /* IJKMediaDemo-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "IJKMediaDemo-Prefix.pch"; sourceTree = "<group>"; };
@@ -162,6 +180,40 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 74D6703E275D259300ABDEE3 /* QHDanmuMan */ = {
+ isa = PBXGroup;
+ children = (
+ 74D6703F275D259300ABDEE3 /* Core */,
+ );
+ path = QHDanmuMan;
+ sourceTree = "<group>";
+ };
+ 74D6703F275D259300ABDEE3 /* Core */ = {
+ isa = PBXGroup;
+ children = (
+ 74D67040275D259300ABDEE3 /* QHDanmuViewCell.m */,
+ 74D67041275D259300ABDEE3 /* QHDanmuView.m */,
+ 74D67042275D259300ABDEE3 /* QHDanmuManager.m */,
+ 74D67043275D259300ABDEE3 /* QHDanmuView.h */,
+ 74D67044275D259300ABDEE3 /* QHDanmuViewCell.h */,
+ 74D67045275D259300ABDEE3 /* QHDanmuManager.h */,
+ );
+ path = Core;
+ sourceTree = "<group>";
+ };
+ 74D67048275D259300ABDEE3 /* QHUtilMan */ = {
+ isa = PBXGroup;
+ children = (
+ 74D67049275D259300ABDEE3 /* QHViewUtil.h */,
+ 74D6704A275D259300ABDEE3 /* NSTimer+QHEOCBlocksSupport.h */,
+ 74D6704B275D259300ABDEE3 /* QHBaseUtil.m */,
+ 74D6704C275D259300ABDEE3 /* QHViewUtil.m */,
+ 74D6704D275D259300ABDEE3 /* QHBaseUtil.h */,
+ 74D6704E275D259300ABDEE3 /* NSTimer+QHEOCBlocksSupport.m */,
+ );
+ path = QHUtilMan;
+ sourceTree = "<group>";
+ };
E63FC27717F032D9003551EB /* Misc */ = {
isa = PBXGroup;
children = (
@@ -254,6 +306,8 @@
E6A9B55C17EDA6AC00A1A500 /* View Controllers */ = {
isa = PBXGroup;
children = (
+ 74D6703E275D259300ABDEE3 /* QHDanmuMan */,
+ 74D67048275D259300ABDEE3 /* QHUtilMan */,
55E809E71B145BC2003E98A5 /* IJKDemoInputURLViewController.h */,
55E809E81B145BC2003E98A5 /* IJKDemoInputURLViewController.m */,
55E809E91B145BC2003E98A5 /* IJKDemoInputURLViewController.xib */,
@@ -329,6 +383,7 @@
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
+ English,
en,
);
mainGroup = E6903EF317EAF70200CFD954;
@@ -401,10 +456,16 @@
buildActionMask = 2147483647;
files = (
55E809F91B15A1DB003E98A5 /* IJKDemoLocalFolderViewController.m in Sources */,
+ 74D67054275D259300ABDEE3 /* NSTimer+QHEOCBlocksSupport.m in Sources */,
+ 74D67050275D259300ABDEE3 /* QHDanmuView.m in Sources */,
+ 74D67052275D259300ABDEE3 /* QHBaseUtil.m in Sources */,
+ 74D6704F275D259300ABDEE3 /* QHDanmuViewCell.m in Sources */,
55E809F61B1480BC003E98A5 /* IJKDemoHistory.m in Sources */,
E6903F0C17EAF70200CFD954 /* main.m in Sources */,
55E809E21B143C47003E98A5 /* IJKDemoMainViewController.m in Sources */,
+ 74D67051275D259300ABDEE3 /* QHDanmuManager.m in Sources */,
E6903F1017EAF70200CFD954 /* IJKAppDelegate.m in Sources */,
+ 74D67053275D259300ABDEE3 /* QHViewUtil.m in Sources */,
E6F524EB1B183A0700B69DC7 /* IJKDemoSampleViewController.m in Sources */,
E6A9B56417EDA72C00A1A500 /* IJKMoviePlayerViewController.m in Sources */,
55E809F11B146C80003E98A5 /* Barcode.m in Sources */,
diff --git a/ios/IJKMediaDemo/IJKMediaDemo/IJKMoviePlayerViewController.m b/ios/IJKMediaDemo/IJKMediaDemo/IJKMoviePlayerViewController.m
index 40f3aa06..0d2707c0 100644
--- a/ios/IJKMediaDemo/IJKMediaDemo/IJKMoviePlayerViewController.m
+++ b/ios/IJKMediaDemo/IJKMediaDemo/IJKMoviePlayerViewController.m
@@ -20,6 +20,17 @@
#import "IJKCommon.h"
#import "IJKDemoHistory.h"
+#import "QHDanmuView.h"
+#import "QHViewUtil.h"
+#import "QHBaseUtil.h"
+
+@interface IJKVideoViewController () <QHDanmuViewDataSource, QHDanmuViewDelegate>
+
+@property (nonatomic, strong) QHDanmuView *danmuView;
+@property (nonatomic, strong) NSTimer *timer;
+
+@end
+
@implementation IJKVideoViewController
- (void)dealloc
@@ -86,6 +97,26 @@
[self.view addSubview:self.mediaControl];
self.mediaControl.delegatePlayer = self.player;
+
+ QHDanmuView *danmuView = [[QHDanmuView alloc] initWithFrame:CGRectZero style:QHDanmuViewStyleCustom];
+ danmuView.backgroundColor = [UIColor clearColor];
+ danmuView.dataSource = self;
+ danmuView.delegate = self;
+ danmuView.danmuPoolMaxCount = 20;
+ danmuView.searchPathwayMode = QHDanmuViewSearchPathwayModeBreadthFirst;
+ [(IJKFFMoviePlayerController *)self.player addDanmu:danmuView];
+ [QHViewUtil fullScreen:danmuView];
+ [danmuView registerClass:[QHDanmuViewCell class] forCellReuseIdentifier:@"1"];
+ self.danmuView = danmuView;
+
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+ __weak typeof(self) weakSelf = self;
+ self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1 repeats:YES block:^(NSTimer * _Nonnull timer) {
+ [weakSelf sendDanmuAction:nil];
+ [weakSelf sendDanmuAction:nil];
+ [weakSelf sendDanmuAction:nil];
+ }];
+ });
}
- (void)viewWillAppear:(BOOL)animated {
@@ -309,4 +340,43 @@
[[NSNotificationCenter defaultCenter]removeObserver:self name:IJKMPMoviePlayerPlaybackStateDidChangeNotification object:_player];
}
+#pragma mark - QHDanmuViewDataSource
+
+- (NSInteger)numberOfPathwaysInDanmuView:(QHDanmuView *)danmuView {
+ return 20;
+}
+
+- (CGFloat)heightOfPathwayCellInDanmuView:(QHDanmuView *)danmuView {
+ CGSize size = [QHBaseUtil getSizeWithString:@"陈" fontSize:15];
+ return size.height;
+}
+
+- (QHDanmuViewCell *)danmuView:(QHDanmuView *)danmuView cellForPathwayWithData:(NSDictionary *)data {
+ QHDanmuViewCell *cell = [danmuView dequeueReusableCellWithIdentifier:@"1"];
+ NSString *n = data[@"n"];
+ NSString *c = data[@"c"];
+ NSString *contentString = [NSString stringWithFormat:@"<font color='#CCCCCC'>%@:</font><font color='#FF0000'>%@</font>", n, c];
+ NSMutableAttributedString *chatData = [NSMutableAttributedString new];
+ [chatData appendAttributedString:[QHBaseUtil toHTML:contentString fontSize:15]];
+ cell.textLabel.attributedText = chatData;
+ return cell;
+}
+
+#pragma mark - QHDanmuViewDelegate
+
+- (CGFloat)danmuView:(QHDanmuView *)danmuView widthForPathwayWithData:(NSDictionary *)data {
+ NSAttributedString *c = [QHBaseUtil toHTML:data[@"c"] fontSize:15];
+ return c.size.width;
+}
+
+#pragma mark - Action
+
+static int pwId = 0;
+
+- (IBAction)sendDanmuAction:(id)sender {
+ pwId++;
+ [_danmuView insertData:@[@{@"n": @"小白", @"c": [NSString stringWithFormat:@"(%i)-讲得挺好,一听就明白。-讲得挺好,一听就明白。-讲得挺好,一听就明白。", pwId]}]];
+// [_danmuView insertDanmuData:@[@{@"n": @"小白", @"c": [NSString stringWithFormat:@"讲得挺好,一听就明白。"]}]];
+}
+
@end
diff --git a/ios/IJKMediaDemo/IJKMediaDemo/QHDanmuMan/Core/QHDanmuManager.h b/ios/IJKMediaDemo/IJKMediaDemo/QHDanmuMan/Core/QHDanmuManager.h
new file mode 100644
index 00000000..7a697a76
--- /dev/null
+++ b/ios/IJKMediaDemo/IJKMediaDemo/QHDanmuMan/Core/QHDanmuManager.h
@@ -0,0 +1,51 @@
+//
+// QHDanmuManager.h
+// QHDanmu2Demo
+//
+// Created by Anakin chen on 2019/1/10.
+// Copyright © 2019 Chen Network Technology. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+// [苹果开发中文网站iOS进阶(一)block与property - CocoaChina_让移动开发更简单](http://www.cocoachina.com/ios/20170503/19165.html)
+typedef CFTimeInterval(^StartTimeBlock)(NSDictionary *data);
+
+@class QHDanmuView;
+
+NS_ASSUME_NONNULL_BEGIN
+
+@protocol QHDanmuManagerDelegate <NSObject>
+
+// 弹幕减少到设定数量时进行回调 [TODO]
+
+@end
+
+@interface QHDanmuManager : NSObject
+
+// 同步点播视频的播放时间点,并进行弹幕选择播放
+@property (nonatomic) CFAbsoluteTime mediaPlayAbsoluteTime;
+
+// 返回数据里面的时间戳,用于排序 & 判断是否显示
+@property (nonatomic, strong) StartTimeBlock startTimeBlock;
+
+- (instancetype)initWithDanmuView:(QHDanmuView *)danmuView;
+/**
+ 批量加入弹幕数据(即包含时间戳的弹幕数组),并进行数组的排序
+ 提醒:控制弹幕的数量
+ */
+- (void)addDanmu:(NSArray<NSDictionary *> *)data;
+/**
+ 加入单条弹幕,可在下一个弹幕时间点显示的弹幕
+ */
+- (void)insertDanmu:(NSDictionary *)data;
+
+- (void)clean;
+- (void)start;
+- (void)stop;
+- (void)resume;
+- (void)pause;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/ios/IJKMediaDemo/IJKMediaDemo/QHDanmuMan/Core/QHDanmuManager.m b/ios/IJKMediaDemo/IJKMediaDemo/QHDanmuMan/Core/QHDanmuManager.m
new file mode 100644
index 00000000..2bb9c2e2
--- /dev/null
+++ b/ios/IJKMediaDemo/IJKMediaDemo/QHDanmuMan/Core/QHDanmuManager.m
@@ -0,0 +1,143 @@
+//
+// QHDanmuManager.m
+// QHDanmu2Demo
+//
+// Created by Anakin chen on 2019/1/10.
+// Copyright © 2019 Chen Network Technology. All rights reserved.
+//
+
+#import "QHDanmuManager.h"
+
+#import "NSTimer+QHEOCBlocksSupport.h"
+#import "QHDanmuView.h"
+
+@interface QHDanmuManager ()
+
+@property (nonatomic, strong) NSMutableArray<NSDictionary *> *danmuDataList;
+
+@property (nonatomic, weak) QHDanmuView *danmuView;
+
+@end
+
+@implementation QHDanmuManager
+
+- (void)dealloc {
+#if DEBUG
+ NSLog(@"%s", __FUNCTION__);
+#endif
+ _danmuDataList = nil;
+ _startTimeBlock = nil;
+}
+
+#pragma mark - Public
+
+- (instancetype)initWithDanmuView:(QHDanmuView *)danmuView {
+ self = [super init];
+ if (self) {
+ _danmuDataList = [NSMutableArray new];
+ _danmuView = danmuView;
+ _mediaPlayAbsoluteTime = 0;
+ _startTimeBlock = ^CFTimeInterval(NSDictionary *data) {
+ return 0;
+ };
+ }
+ return self;
+}
+
+- (void)addDanmu:(NSArray<NSDictionary *> *)data {
+ if (data == nil || data.count <= 0) {
+ return;
+ }
+ NSArray *dataTemp = [data sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
+ CFTimeInterval v1 = self.startTimeBlock(obj1);
+ CFTimeInterval v2 = self.startTimeBlock(obj2);
+
+ NSComparisonResult result = v1 <= v2 ? NSOrderedAscending : NSOrderedDescending;
+
+ return result;
+ }];
+
+// [_danmuDataList addObjectsFromArray:dataTemp];
+
+ // 合并排序
+ _danmuDataList = [self insertSort:_danmuDataList secondArray:dataTemp];
+}
+
+- (void)insertDanmu:(NSDictionary *)data {
+ [_danmuView insertDataInFirst:data];
+}
+
+// [TODO] 增加选取限制范围(即向前几秒或者几条弹幕),处理 pause/stop 时候时间过久的数据堆积
+- (void)setMediaPlayAbsoluteTime:(CFAbsoluteTime)mediaPlayAbsoluteTime {
+ _mediaPlayAbsoluteTime = mediaPlayAbsoluteTime;
+ int index = -1;
+ if (_danmuDataList.count > 0) {
+ for (int i = 0; i < _danmuDataList.count; i++) {
+ NSDictionary *danmuData = _danmuDataList[i];
+ CFTimeInterval startTime = 0;
+ startTime = _startTimeBlock(danmuData);
+
+ if (startTime <= mediaPlayAbsoluteTime) {
+ index = i;
+ }
+ else {
+ break;
+ }
+ }
+ if (index >= 0) {
+ NSArray *inData = [_danmuDataList subarrayWithRange:NSMakeRange(0, index + 1)];
+ [_danmuView insertData:inData];
+ [_danmuDataList removeObjectsInArray:inData];
+ }
+ }
+}
+
+- (void)clean {
+ [_danmuDataList removeAllObjects];
+}
+
+- (void)start {
+ [_danmuView start];
+}
+
+- (void)stop {
+ [_danmuView stop];
+}
+
+- (void)resume {
+ [_danmuView resume];
+}
+
+- (void)pause {
+ [_danmuView pause];
+}
+
+#pragma mark - Private
+
+#pragma mark - Util
+
+// [iOS算法总结-归并排序 - 简书](https://www.jianshu.com/p/04d9480a0633)
+- (NSMutableArray<NSDictionary *> *)insertSort:(NSArray<NSDictionary *> *)firstArray secondArray:(NSArray<NSDictionary *> *)secondArray{
+ NSMutableArray *resultArray = [NSMutableArray array];
+ NSInteger firstIndex = 0, secondIndex = 0;
+ while (firstIndex < firstArray.count && secondIndex < secondArray.count) {
+ if ([firstArray[firstIndex][@"v"] doubleValue] < [secondArray[secondIndex][@"v"] doubleValue]) {
+ [resultArray addObject:firstArray[firstIndex]];
+ firstIndex++;
+ } else {
+ [resultArray addObject:secondArray[secondIndex]];
+ secondIndex++;
+ }
+ }
+ while (firstIndex < firstArray.count) {
+ [resultArray addObject:firstArray[firstIndex]];
+ firstIndex++;
+ }
+ while (secondIndex < secondArray.count) {
+ [resultArray addObject:secondArray[secondIndex]];
+ secondIndex++;
+ }
+ return resultArray;
+}
+
+@end
diff --git a/ios/IJKMediaDemo/IJKMediaDemo/QHDanmuMan/Core/QHDanmuView.h b/ios/IJKMediaDemo/IJKMediaDemo/QHDanmuMan/Core/QHDanmuView.h
new file mode 100644
index 00000000..592d4536
--- /dev/null
+++ b/ios/IJKMediaDemo/IJKMediaDemo/QHDanmuMan/Core/QHDanmuView.h
@@ -0,0 +1,97 @@
+//
+// QHDanmuView.h
+// QHDanmu2Demo
+//
+// Created by Anakin chen on 2019/1/4.
+// Copyright © 2019 Chen Network Technology. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+#import "QHDanmuViewCell.h"
+
+@class QHDanmuView;
+
+@protocol QHDanmuViewDataSource <NSObject>
+
+@required
+
+- (NSInteger)numberOfPathwaysInDanmuView:(QHDanmuView * _Nonnull)danmuView;
+
+- (CGFloat)heightOfPathwayCellInDanmuView:(QHDanmuView * _Nonnull)danmuView;
+
+- (QHDanmuViewCell * _Nullable)danmuView:(QHDanmuView * _Nonnull)danmuView cellForPathwayWithData:(NSDictionary * _Nonnull)data;
+
+@optional
+
+/**
+ 设置弹幕飞行的时间
+ */
+- (CFTimeInterval)playUseTimeOfPathwayCellInDanmuView:(QHDanmuView * _Nonnull)danmuView;
+
+/**
+ 设置弹幕触发时是否在找不到轨道后的处理,YES 为 删除,NO 为 等待,默认 NO
+ */
+- (BOOL)waitWhenNowHasNoPathwayInDanmuView:(QHDanmuView * _Nonnull)danmuView withData:(NSDictionary * _Nonnull)data;
+
+@end
+
+@protocol QHDanmuViewDelegate <NSObject>
+
+- (CGFloat)danmuView:(QHDanmuView * _Nonnull)danmuView widthForPathwayWithData:(NSDictionary * _Nonnull)data;
+
+@end
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef NS_ENUM(NSInteger, QHDanmuViewStyle) {
+ QHDanmuViewStyleCustom,
+};
+
+typedef NS_ENUM(NSInteger, QHDanmuViewStatus) {
+ QHDanmuViewStatusPlay,
+ QHDanmuViewStatusStop,
+ QHDanmuViewStatusPause,
+};
+
+typedef NS_ENUM(NSInteger, QHDanmuViewSearchPathwayMode) {
+ QHDanmuViewSearchPathwayModeDepthFirst, // 深度优先
+ QHDanmuViewSearchPathwayModeBreadthFirst, // 广度优先
+ // 随机选择轨道 [TODO]
+};
+
+@interface QHDanmuView : UIView
+
+@property (nonatomic, readonly) QHDanmuViewStyle style;
+@property (nonatomic, readonly) QHDanmuViewStatus status;
+
+@property (nonatomic, weak, nullable) id <QHDanmuViewDataSource> dataSource;
+@property (nonatomic, weak, nullable) id <QHDanmuViewDelegate> delegate;
+// 弹幕池容量,默认 10 条
+@property (nonatomic) NSUInteger danmuPoolMaxCount;
+// 轨道搜索模式,默认 DepthFirst
+@property (nonatomic) QHDanmuViewSearchPathwayMode searchPathwayMode;
+// 复用弹幕池容量,默认 100
+@property (nonatomic) NSUInteger reusableCellMaxCount;
+
+- (id)initWithFrame:(CGRect)frame style:(QHDanmuViewStyle)theStyle;
+
+- (void)registerClass:(nullable Class)cellClass forCellReuseIdentifier:(nonnull NSString *)identifier;
+
+- (nullable __kindof QHDanmuViewCell *)dequeueReusableCellWithIdentifier:(nonnull NSString *)identifier;
+
+- (void)insertData:(NSArray<NSDictionary *> *)data;
+/**
+ 弹幕加入到弹幕池最前面
+ */
+- (void)insertDataInFirst:(NSDictionary *)data;
+- (void)cleanData;
+
+- (void)start;
+- (void)stop;
+- (void)resume;
+- (void)pause;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/ios/IJKMediaDemo/IJKMediaDemo/QHDanmuMan/Core/QHDanmuView.m b/ios/IJKMediaDemo/IJKMediaDemo/QHDanmuMan/Core/QHDanmuView.m
new file mode 100644
index 00000000..eb64a222
--- /dev/null
+++ b/ios/IJKMediaDemo/IJKMediaDemo/QHDanmuMan/Core/QHDanmuView.m
@@ -0,0 +1,501 @@
+//
+// QHDanmuView.m
+// QHDanmu2Demo
+//
+// Created by Anakin chen on 2019/1/4.
+// Copyright © 2019 Chen Network Technology. All rights reserved.
+//
+
+#import "QHDanmuView.h"
+
+#import "NSTimer+QHEOCBlocksSupport.h"
+#import "QHViewUtil.h"
+
+// [UITableView的Cell复用原理和源码分析 - 简书](https://www.jianshu.com/p/5b0e1ca9b673)
+// [Chameleon/UITableView.m at master · BigZaphod/Chameleon](https://github.com/BigZaphod/Chameleon/blob/master/UIKit/Classes/UITableView.m)
+
+struct QHDanmuViewDataSourceHas {
+ NSUInteger playUseTimeOfPathwayCell;
+ NSUInteger waitWhenNowHasNoPathway;
+};
+typedef struct QHDanmuViewDataSourceHas QHDanmuViewDataSourceHas;
+
+#define kQHDanmuPlayUseTime 6.0
+#define kQHDanmuPoolMaxCount 10
+#define kQHReusableCellMaxCount 100
+
+@interface QHDanmuView ()
+
+// [Objective-C 内存管理——你需要知道的一切 - skyline75489 - SegmentFault 思否](https://segmentfault.com/a/1190000004943276)
+
+@property (nonatomic, strong) UIView *contentView;
+
+@property (nonatomic, strong) NSMutableArray<NSDictionary *> *danmuDataList;
+@property (nonatomic, strong) NSTimer *danmuTimer;
+@property (nonatomic, readwrite) QHDanmuViewStyle style;
+@property (nonatomic, readwrite) QHDanmuViewStatus status;
+
+// [UITableviewCell复用机制 - 简书](https://www.jianshu.com/p/1046c741fce1)
+@property (nonatomic, strong) NSMutableArray *reusableCells;
+@property (nonatomic, strong) NSMutableDictionary *reusableCellsIdentifierDic;
+@property (nonatomic, strong) NSMutableDictionary *cachedCellsParam;
+
+@property (nonatomic) NSInteger pathwayCount;
+@property (nonatomic) CGFloat pathwayHeight;
+@property (nonatomic) QHDanmuViewDataSourceHas dataSourceHas;
+
+@property (nonatomic) dispatch_queue_t danmuQueue;
+
+@end
+
+@implementation QHDanmuView
+
+- (void)dealloc {
+#if DEBUG
+ NSLog(@"%s", __FUNCTION__);
+#endif
+ [self p_closeTimer];
+ _danmuTimer = nil;
+
+ _reusableCells = nil;
+ _reusableCellsIdentifierDic = nil;
+ _cachedCellsParam = nil;
+}
+
+- (instancetype)init {
+ self = [super init];
+ if (self) {
+ _style = QHDanmuViewStyleCustom;
+ [self p_setup];
+ }
+ return self;
+}
+
+// [iOS - layoutSubviews总结(作用及调用机制) - 简书](https://www.jianshu.com/p/a2acc4c7dc4b)
+- (void)layoutSubviews {
+ [super layoutSubviews];
+}
+
+#pragma mark - Public
+
+- (instancetype)initWithFrame:(CGRect)frame {
+ return [self initWithFrame:frame style:QHDanmuViewStyleCustom];
+}
+
+- (id)initWithFrame:(CGRect)frame style:(QHDanmuViewStyle)theStyle {
+ self = [super initWithFrame:frame];
+ if (self) {
+ _style = theStyle;
+ [self p_setup];
+ }
+ return self;
+}
+
+- (void)insertData:(NSArray<NSDictionary *> *)data {
+ if (data == nil || data.count <= 0) {
+ return;
+ }
+ if (_danmuDataList.count >= _danmuPoolMaxCount) {
+ return;
+ }
+ if (![NSThread isMainThread]) {
+ [self performSelectorOnMainThread:_cmd withObject:data waitUntilDone:NO];
+ return;
+ }
+ NSArray *newData = data;
+ if (data.count > 1) {
+ NSUInteger s = self.danmuPoolMaxCount - self.danmuDataList.count;
+ if (data.count > s) {
+ newData = [data subarrayWithRange:NSMakeRange(0, s)];
+ }
+ }
+ [self.danmuDataList addObjectsFromArray:newData];
+
+ [self p_goDanmuTimer];
+}
+
+- (void)insertDataInFirst:(NSDictionary *)data {
+ if (data == nil) {
+ return;
+ }
+ if (![NSThread isMainThread]) {
+ [self performSelectorOnMainThread:_cmd withObject:data waitUntilDone:NO];
+ return;
+ }
+
+ [self.danmuDataList insertObject:data atIndex:0];
+
+ if (_danmuDataList.count >= _danmuPoolMaxCount) {
+ [self.danmuDataList removeObjectAtIndex:(_danmuDataList.count - 1)];
+ }
+ [self p_goDanmuTimer];
+}
+
+- (void)registerClass:(nullable Class)cellClass forCellReuseIdentifier:(nonnull NSString *)identifier {
+ [_reusableCellsIdentifierDic setObject:cellClass forKey:identifier];
+ QHDanmuViewCell *cell = [(QHDanmuViewCell *)[cellClass alloc] _qhDanmuInitWithReuseIdentifier:identifier];
+ [_reusableCells addObject:cell];
+ cell = nil;
+}
+
+- (nullable __kindof QHDanmuViewCell *)dequeueReusableCellWithIdentifier:(nonnull NSString *)identifier {
+ __block QHDanmuViewCell *cell = nil;
+ Class class = [_reusableCellsIdentifierDic objectForKey:identifier];
+ if (class != nil) {
+ [_reusableCells enumerateObjectsUsingBlock:^(QHDanmuViewCell *_Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
+ if (obj.reuseIdentifier == identifier) {
+ cell = obj;
+ *stop = NO;
+ }
+ }];
+ if (cell != nil) {
+ [_reusableCells removeObject:cell];
+ }
+ else {
+ cell = [(QHDanmuViewCell *)[class alloc] _qhDanmuInitWithReuseIdentifier:identifier];
+ }
+ }
+ return cell;
+}
+
+- (void)cleanData {
+ [self p_cleanData];
+}
+
+- (void)start {
+ if (_status == QHDanmuViewStatusPause) {
+ [self p_resume];
+ }
+ else if (_status == QHDanmuViewStatusStop) {
+ [self p_play];
+ }
+}
+
+- (void)stop {
+ if (_status == QHDanmuViewStatusStop) {
+ return;
+ }
+ _status = QHDanmuViewStatusStop;
+ [self p_cleanData];
+}
+
+- (void)resume {
+ [self p_resume];
+}
+
+- (void)pause {
+ if (_status != QHDanmuViewStatusPlay) {
+ return;
+ }
+ _status = QHDanmuViewStatusPause;
+ [self p_closeTimer];
+ for (QHDanmuViewCell *cell in _contentView.subviews) {
+ CALayer *layer = cell.layer;
+ CGRect rect = cell.frame;
+ if (layer.presentationLayer) {
+ rect = ((CALayer *)layer.presentationLayer).frame;
+ }
+ cell.frame = rect;
+ [cell.layer removeAllAnimations];
+ }
+}
+
+#pragma mark - Private
+
+- (void)p_setup {
+ [self p_setupData];
+ [self p_setupUI];
+}
+
+- (void)p_setupData {
+// _style = QHDanmuViewStyleCustom;
+ _status = QHDanmuViewStatusPlay;
+
+ _danmuDataList = [NSMutableArray new];
+
+ _reusableCells = [NSMutableArray new];
+ _reusableCellsIdentifierDic = [NSMutableDictionary new];
+ _cachedCellsParam = [NSMutableDictionary new];
+
+ _pathwayCount = 0;
+ _pathwayHeight = 0;
+ _danmuPoolMaxCount = kQHDanmuPoolMaxCount;
+ _reusableCellMaxCount = kQHReusableCellMaxCount;
+ _searchPathwayMode = QHDanmuViewSearchPathwayModeDepthFirst;
+
+ _dataSourceHas.playUseTimeOfPathwayCell = 0;
+
+ _danmuQueue = dispatch_queue_create("com.danmu.queue", NULL);
+}
+
+- (void)p_setupUI {
+ _contentView = [[UIView alloc] init];
+ _contentView.backgroundColor = [UIColor clearColor];
+ _contentView.clipsToBounds = YES;
+ [self addSubview:_contentView];
+ [QHViewUtil fullScreen:_contentView];
+}
+
+- (void)p_closeTimer {
+ [_danmuTimer invalidate];
+ _danmuTimer = nil;
+}
+
+- (void)p_goDanmuTimer {
+ if (_status != QHDanmuViewStatusPlay) {
+ return;
+ }
+ if (_danmuTimer == nil) {
+ __weak typeof(self) weakSelf = self;
+ _danmuTimer = [NSTimer qheoc_scheduledTimerWithTimeInterval:0.2 block:^{
+ dispatch_sync(weakSelf.danmuQueue, ^{
+ for (int i = 0; i < weakSelf.pathwayCount; i++) {
+ BOOL bAction = [weakSelf p_danmuAction];
+ if (bAction == NO) {
+ break;
+ }
+ }
+ });
+ } repeats:YES];
+ }
+}
+
+- (BOOL)p_danmuAction {
+
+ if (_danmuDataList.count <= 0 || _status != QHDanmuViewStatusPlay) {
+ [self p_closeTimer];
+ return NO;
+ }
+
+ UIApplicationState state = [UIApplication sharedApplication].applicationState;
+ if (state == UIApplicationStateBackground) {
+ return NO;
+ }
+
+ NSDictionary *data = _danmuDataList.firstObject;
+ if (data == nil) {
+ return NO;
+ }
+
+ CFTimeInterval playUseTime = kQHDanmuPlayUseTime;
+ if (_dataSourceHas.playUseTimeOfPathwayCell == 1) {
+ playUseTime = [_dataSource playUseTimeOfPathwayCellInDanmuView:self];
+ }
+
+ QHDanmuCellParam newParam = [self p_danmuParamWithPlayUseTime:playUseTime];
+
+ if (newParam.pathwayNumber == -1) {
+ if (_dataSourceHas.waitWhenNowHasNoPathway == 1) {
+ if ([_dataSource waitWhenNowHasNoPathwayInDanmuView:self withData:data] == NO) {
+ [_danmuDataList removeObject:data];
+ }
+ }
+ return NO;
+ }
+
+ QHDanmuViewCell *cell = [_dataSource danmuView:self cellForPathwayWithData:data];
+ if (cell == nil) {
+ [_danmuDataList removeObject:data];
+ return NO;
+ }
+ cell.frame = CGRectMake(self.frame.size.width, (_pathwayHeight * newParam.pathwayNumber), newParam.width, _pathwayHeight);
+ [_contentView addSubview:cell];
+
+ [self p_danmuAnimationOfFlyWithCell:cell param:newParam playUseTime:playUseTime];
+
+ // 使用完清除弹幕池数据
+ [_danmuDataList removeObject:data];
+ return YES;
+}
+
+- (QHDanmuCellParam)p_danmuParamWithPlayUseTime:(CGFloat)playUseTime {
+ NSDictionary *data = _danmuDataList.firstObject;
+ QHDanmuCellParam newParam;
+ newParam.pathwayNumber = -1;
+
+ // 广度优先:先搜索是否有空闲的轨道,有则使用,无则再走深度优先
+ if (_searchPathwayMode == QHDanmuViewSearchPathwayModeBreadthFirst) {
+ for (int i = 0; i < _pathwayCount; i++) {
+ NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
+ id obj = [_cachedCellsParam objectForKey:indexPath];
+ if ([obj isKindOfClass:[NSNull class]] == YES) {
+ // 记录时间
+ newParam.startTime = CFAbsoluteTimeGetCurrent();
+ // 记录宽度 & 计算速度
+ newParam.width = [_delegate danmuView:self widthForPathwayWithData:data];
+ newParam.speed = (newParam.width + self.frame.size.width) / playUseTime;
+ newParam.pathwayNumber = i;
+
+ break;
+ }
+ }
+
+ if (newParam.pathwayNumber >= 0) {
+ return newParam;
+ }
+ }
+
+ for (int i = 0; i < _pathwayCount; i++) {
+ // 记录时间
+ newParam.startTime = CFAbsoluteTimeGetCurrent();
+
+ NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
+ id obj = [_cachedCellsParam objectForKey:indexPath];
+ if ([obj isKindOfClass:[NSNull class]] == YES) {
+ // 记录宽度 & 计算速度
+ newParam.width = [_delegate danmuView:self widthForPathwayWithData:data];
+ newParam.speed = (newParam.width + self.frame.size.width) / playUseTime;
+ newParam.pathwayNumber = i;
+ }
+ else if ([obj isKindOfClass:[NSValue class]] == YES) {
+ QHDanmuCellParam lastParam;
+ [(NSValue *)obj getValue:&lastParam];
+
+ CFTimeInterval spaceTime = newParam.startTime - lastParam.startTime;
+ if (spaceTime * lastParam.speed >= lastParam.width) {
+
+ // 记录宽度 & 计算速度
+ newParam.width = [_delegate danmuView:self widthForPathwayWithData:data];
+ newParam.speed = (newParam.width + self.frame.size.width) / playUseTime;
+
+ // 最后一个弹幕已完全显示在轨道
+ // 如果最后弹幕的速度大于新的弹幕速度,则该轨道可与使用
+ if (lastParam.speed >= newParam.speed) {
+ newParam.pathwayNumber = lastParam.pathwayNumber;
+ }
+ else {
+ // 最后一个弹幕剩余的滑动时间,新弹幕是否能在显示区域追上,判断新弹幕需要的时间与之比较。小于等于,则追不上,该轨道可使用;反之不可使用。
+ CGFloat useTimeInScreen = self.frame.size.width / newParam.speed;
+ if (useTimeInScreen >= (playUseTime - spaceTime)) {
+ newParam.pathwayNumber = lastParam.pathwayNumber;
+ }
+ }
+ }
+ else {
+ // 最后一个弹幕还没有完全显示在轨道(即尾部还未显示),则该轨道不可使用
+ }
+ }
+
+ if (newParam.pathwayNumber >= 0) {
+ break;
+ }
+ }
+
+ // [CGGeometry - NSHipster](https://nshipster.cn/cggeometry/#%E5%B8%B8%E9%87%8F)
+
+ return newParam;
+}
+
+- (void)p_danmuAnimationOfFlyWithCell:(QHDanmuViewCell *)cell param:(QHDanmuCellParam)param playUseTime:(CFTimeInterval)playUseTime {
+
+ cell.param = param;
+
+ NSValue *newParamValue = [NSValue valueWithBytes:&param objCType:@encode(QHDanmuCellParam)];
+ NSIndexPath *indexPath = [NSIndexPath indexPathForRow:param.pathwayNumber inSection:0];
+ [_cachedCellsParam setObject:newParamValue forKey:indexPath];
+
+ CGRect goFrame = cell.frame;
+ goFrame.origin.x = -cell.frame.size.width;
+ [UIView animateWithDuration:playUseTime delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
+ cell.frame = goFrame;
+ } completion:^(BOOL finished) {
+ if (finished) {
+ // 只有 cell 设置了 reuseIdentifier 才能进入复用池
+ if (cell.reuseIdentifier != nil && cell.reuseIdentifier.length >= 0) {
+ if (self.reusableCells.count < self.reusableCellMaxCount) {
+ { // 还原
+ QHDanmuCellParam p = cell.param;
+ p.pathwayNumber = -1;
+ p.startTime = 0;
+ p.speed = 0;
+ p.width = 0;
+ cell.param = p;
+ }
+ [self.reusableCells addObject:cell];
+ }
+ }
+ [cell removeFromSuperview];
+ NSIndexPath *indexPath = [NSIndexPath indexPathForRow:param.pathwayNumber inSection:0];
+ id obj = [self.cachedCellsParam objectForKey:indexPath];
+ if ([obj isKindOfClass:[NSValue class]] == YES) {
+ QHDanmuCellParam deleteParam;
+ [(NSValue *)obj getValue:&deleteParam];
+ if (deleteParam.startTime == param.startTime && deleteParam.pathwayNumber == param.pathwayNumber) {
+ [self.cachedCellsParam setObject:[NSNull null] forKey:indexPath];
+ }
+ }
+ }
+ }];
+}
+
+- (void)p_resetCachedCellsParam {
+ [_cachedCellsParam removeAllObjects];
+ for (int i = 0; i < _pathwayCount; i++) {
+ NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
+ [_cachedCellsParam setObject:[NSNull null] forKey:indexPath];
+ }
+}
+
+- (void)p_cleanData {
+ [self p_closeTimer];
+ /*
+ 1、使用 dispatch_barrier_async & dispatch_sync 解决数据竞争,对数组的删除导致崩溃
+ [iOS-线程安全NSMutableArray - 简书](https://www.jianshu.com/p/0b5a97720ebe)
+ 2、Group 或者 dispatch_sync 会出现 EXC_BAD_INSTRUCTION 崩溃
+ [ios - EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) on dispatch_semaphore_dispose - Stack Overflow](https://stackoverflow.com/questions/24337791/exc-bad-instruction-code-exc-i386-invop-subcode-0x0-on-dispatch-semaphore-dis)
+ 3、NSLock 也会死锁
+ [iOS 多线程基础 四、多线程安全——线程锁 - 简书](https://www.jianshu.com/p/27af7fae9947)
+ 4、使用 @synchronized 则依然会有数据竞争问题
+ */
+ dispatch_barrier_async(_danmuQueue, ^{
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self.contentView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
+ });
+ [self.danmuDataList removeAllObjects];
+ [self.reusableCells removeAllObjects];
+ [self p_resetCachedCellsParam];
+ });
+}
+
+- (void)p_play {
+ _status = QHDanmuViewStatusPlay;
+ [self p_goDanmuTimer];
+}
+
+- (void)p_resume {
+ if (_status != QHDanmuViewStatusPause) {
+ return;
+ }
+ CFTimeInterval playUseTime = kQHDanmuPlayUseTime;
+ if (_dataSourceHas.playUseTimeOfPathwayCell == 1) {
+ playUseTime = [_dataSource playUseTimeOfPathwayCellInDanmuView:self];
+ }
+ for (QHDanmuViewCell *cell in _contentView.subviews) {
+ // 计算已经运行的时间
+ CFTimeInterval hadPlayUseTime = (self.frame.size.width - cell.frame.origin.x) / cell.param.speed;
+ // 计算还需多少时间
+ CFTimeInterval needPlayUseTime = MAX(playUseTime - hadPlayUseTime, 0.0);
+
+ QHDanmuCellParam newParam = cell.param;
+ newParam.startTime = CFAbsoluteTimeGetCurrent() - hadPlayUseTime;
+
+ [self p_danmuAnimationOfFlyWithCell:cell param:newParam playUseTime:needPlayUseTime];
+ }
+ [self p_play];
+}
+
+#pragma mark - Set
+
+- (void)setDataSource:(id<QHDanmuViewDataSource>)dataSource {
+ _dataSource = dataSource;
+
+ _pathwayCount = [_dataSource numberOfPathwaysInDanmuView:self];
+ _pathwayHeight = [_dataSource heightOfPathwayCellInDanmuView:self];
+
+ _dataSourceHas.playUseTimeOfPathwayCell = [_dataSource respondsToSelector:@selector(playUseTimeOfPathwayCellInDanmuView:)];
+ _dataSourceHas.waitWhenNowHasNoPathway = [_dataSource respondsToSelector:@selector(waitWhenNowHasNoPathwayInDanmuView:withData:)];
+
+ [self p_resetCachedCellsParam];
+}
+
+@end
diff --git a/ios/IJKMediaDemo/IJKMediaDemo/QHDanmuMan/Core/QHDanmuViewCell.h b/ios/IJKMediaDemo/IJKMediaDemo/QHDanmuMan/Core/QHDanmuViewCell.h
new file mode 100644
index 00000000..3e52b232
--- /dev/null
+++ b/ios/IJKMediaDemo/IJKMediaDemo/QHDanmuMan/Core/QHDanmuViewCell.h
@@ -0,0 +1,45 @@
+//
+// QHDanmuViewCell.h
+// QHDanmu2Demo
+//
+// Created by Anakin chen on 2019/1/4.
+// Copyright © 2019 Chen Network Technology. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef NS_ENUM(NSInteger, QHDanmuViewCellStyle) {
+ QHDanmuViewCellStyleDefault,
+ QHDanmuViewCellStyleCustom
+};
+
+// [【iOS开发】结构体如何存入数组中 - wm9028的专栏 - CSDN博客](https://blog.csdn.net/wm9028/article/details/50681773)
+struct QHDanmuCellParam {
+ CGFloat speed;
+ CGFloat width;
+ NSInteger pathwayNumber;
+ CFTimeInterval startTime;
+};
+typedef struct QHDanmuCellParam QHDanmuCellParam;
+
+@interface QHDanmuViewCell : UIView
+
+@property (nonatomic, readonly, copy, nullable) NSString *reuseIdentifier;
+
+@property (nonatomic, readonly, strong) UIView *contentView;
+@property (nonatomic, readonly, strong, nullable) UILabel *textLabel;
+@property (nonatomic) QHDanmuCellParam param;
+
+- (instancetype)initWithStyle:(QHDanmuViewCellStyle)style reuseIdentifier:(nullable NSString *)reuseIdentifier;
+
+@end
+
+@interface QHDanmuViewCell (Private)
+
+- (instancetype)_qhDanmuInitWithReuseIdentifier:(nullable NSString *)reuseIdentifier;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/ios/IJKMediaDemo/IJKMediaDemo/QHDanmuMan/Core/QHDanmuViewCell.m b/ios/IJKMediaDemo/IJKMediaDemo/QHDanmuMan/Core/QHDanmuViewCell.m
new file mode 100644
index 00000000..69c764b3
--- /dev/null
+++ b/ios/IJKMediaDemo/IJKMediaDemo/QHDanmuMan/Core/QHDanmuViewCell.m
@@ -0,0 +1,71 @@
+//
+// QHDanmuViewCell.m
+// QHDanmu2Demo
+//
+// Created by Anakin chen on 2019/1/4.
+// Copyright © 2019 Chen Network Technology. All rights reserved.
+//
+
+#import "QHDanmuViewCell.h"
+
+#import "QHViewUtil.h"
+
+@interface QHDanmuViewCell ()
+
+@property (nonatomic, readwrite, copy, nullable) NSString *reuseIdentifier;
+
+@property (nonatomic, readwrite, strong) UIView *contentView;
+@property (nonatomic, readwrite, strong, nullable) UILabel *textLabel;
+
+@property (nonatomic) QHDanmuViewCellStyle style;
+
+@end
+
+@implementation QHDanmuViewCell
+
+- (void)dealloc {
+ _reuseIdentifier = nil;
+
+ _contentView = nil;
+ _textLabel = nil;
+}
+
+- (instancetype)init {
+ return [self initWithStyle:QHDanmuViewCellStyleDefault reuseIdentifier:nil];
+}
+
+- (instancetype)initWithStyle:(QHDanmuViewCellStyle)style reuseIdentifier:(nullable NSString *)reuseIdentifier {
+ self = [super init];
+ if (self) {
+ self.reuseIdentifier = reuseIdentifier;
+ self.style = style;
+ _contentView = [[UIView alloc] init];
+ [self addSubview:_contentView];
+ [QHViewUtil fullScreen:_contentView];
+ }
+ return self;
+}
+
+#pragma mark - Get
+
+- (UILabel *)textLabel {
+ if (_style == QHDanmuViewCellStyleCustom) {
+ return nil;
+ }
+ if (_textLabel == nil) {
+ _textLabel = [[UILabel alloc] init];
+ [_contentView addSubview:_textLabel];
+ [QHViewUtil fullScreen:_textLabel];
+ }
+ return _textLabel;
+}
+
+@end
+
+@implementation QHDanmuViewCell (Private)
+
+- (instancetype)_qhDanmuInitWithReuseIdentifier:(nullable NSString *)reuseIdentifier {
+ return [self initWithStyle:QHDanmuViewCellStyleDefault reuseIdentifier:reuseIdentifier];
+}
+
+@end
diff --git a/ios/IJKMediaDemo/IJKMediaDemo/QHUtilMan/NSTimer+QHEOCBlocksSupport.h b/ios/IJKMediaDemo/IJKMediaDemo/QHUtilMan/NSTimer+QHEOCBlocksSupport.h
new file mode 100644
index 00000000..5885905e
--- /dev/null
+++ b/ios/IJKMediaDemo/IJKMediaDemo/QHUtilMan/NSTimer+QHEOCBlocksSupport.h
@@ -0,0 +1,26 @@
+//
+// NSTimer+QHEOCBlocksSupport.h
+// QHChatScreenDemo
+//
+// Created by Anakin chen on 2018/2/6.
+// Copyright © 2018年 Anakin Network Technology. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+/**
+ 生成一个不会导致循环依赖的Timer
+ */
+@interface NSTimer (QHEOCBlocksSupport)
+
+/**
+ 生成一个不会导致循环依赖的Timer
+
+ @param interval 触发时间
+ @param block 触发后的操作
+ @param repeats 是否重复触发
+ @return timer
+ */
++ (NSTimer *)qheoc_scheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void (^)(void))block repeats:(BOOL)repeats;
+
+@end
diff --git a/ios/IJKMediaDemo/IJKMediaDemo/QHUtilMan/NSTimer+QHEOCBlocksSupport.m b/ios/IJKMediaDemo/IJKMediaDemo/QHUtilMan/NSTimer+QHEOCBlocksSupport.m
new file mode 100644
index 00000000..658c0962
--- /dev/null
+++ b/ios/IJKMediaDemo/IJKMediaDemo/QHUtilMan/NSTimer+QHEOCBlocksSupport.m
@@ -0,0 +1,24 @@
+//
+// NSTimer+QHEOCBlocksSupport.m
+// QHChatScreenDemo
+//
+// Created by Anakin chen on 2018/2/6.
+// Copyright © 2018年 Anakin Network Technology. All rights reserved.
+//
+
+#import "NSTimer+QHEOCBlocksSupport.h"
+
+@implementation NSTimer (QHEOCBlocksSupport)
+
++ (NSTimer *)qheoc_scheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void (^)(void))block repeats:(BOOL)repeats {
+ return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(qheoc_blockInvoke:) userInfo:[block copy] repeats:repeats];
+}
+
++ (void)qheoc_blockInvoke:(NSTimer *)timer {
+ void (^block)(void) = timer.userInfo;
+ if (block) {
+ block();
+ }
+}
+
+@end
diff --git a/ios/IJKMediaDemo/IJKMediaDemo/QHUtilMan/QHBaseUtil.h b/ios/IJKMediaDemo/IJKMediaDemo/QHUtilMan/QHBaseUtil.h
new file mode 100644
index 00000000..bbe85b3e
--- /dev/null
+++ b/ios/IJKMediaDemo/IJKMediaDemo/QHUtilMan/QHBaseUtil.h
@@ -0,0 +1,23 @@
+//
+// QHBaseUtil.h
+// QHDanmu2Demo
+//
+// Created by Anakin chen on 2019/1/7.
+// Copyright © 2019 Chen Network Technology. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <AVFoundation/AVFoundation.h>
+#import <UIKit/UIKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface QHBaseUtil : NSObject
+
++ (CGSize)getSizeWithString:(NSString *)str fontSize:(CGFloat)fontSize;
+
++ (NSAttributedString *)toHTML:(NSString *)content fontSize:(CGFloat)fontSize;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/ios/IJKMediaDemo/IJKMediaDemo/QHUtilMan/QHBaseUtil.m b/ios/IJKMediaDemo/IJKMediaDemo/QHUtilMan/QHBaseUtil.m
new file mode 100644
index 00000000..bec21e43
--- /dev/null
+++ b/ios/IJKMediaDemo/IJKMediaDemo/QHUtilMan/QHBaseUtil.m
@@ -0,0 +1,31 @@
+//
+// QHBaseUtil.m
+// QHDanmu2Demo
+//
+// Created by Anakin chen on 2019/1/7.
+// Copyright © 2019 Chen Network Technology. All rights reserved.
+//
+
+#import "QHBaseUtil.h"
+
+@implementation QHBaseUtil
+
++ (CGSize)getSizeWithString:(NSString *)str fontSize:(CGFloat)fontSize {
+ CGSize s;
+ s = [str sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:fontSize]}];
+ return s;
+}
+
++ (NSAttributedString *)toHTML:(NSString *)content fontSize:(CGFloat)fontSize {
+ if (content == nil || content.length <= 0) {
+ return nil;
+ }
+ NSError *error = nil;
+ NSAttributedString *attributedString = [[NSAttributedString alloc] initWithData:[content dataUsingEncoding:NSUnicodeStringEncoding] options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSFontAttributeName: [UIFont systemFontOfSize:fontSize], NSForegroundColorAttributeName: [UIColor whiteColor]} documentAttributes:nil error:&error];
+ if (error != nil) {
+ return nil;
+ }
+ return attributedString;
+}
+
+@end
diff --git a/ios/IJKMediaDemo/IJKMediaDemo/QHUtilMan/QHViewUtil.h b/ios/IJKMediaDemo/IJKMediaDemo/QHUtilMan/QHViewUtil.h
new file mode 100644
index 00000000..99d4fbb9
--- /dev/null
+++ b/ios/IJKMediaDemo/IJKMediaDemo/QHUtilMan/QHViewUtil.h
@@ -0,0 +1,25 @@
+//
+// QHViewUtil.h
+// QHChatDemo
+//
+// Created by Anakin chen on 2018/12/21.
+// Copyright © 2018 Chen Network Technology. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+#define QHCOLOR_RGBA(R/*红*/, G/*绿*/, B/*蓝*/, A/*透明*/) \
+[UIColor colorWithRed:R/255.f green:G/255.f blue:B/255.f alpha:A]
+
+@interface QHViewUtil : NSObject
+
++ (void)fullScreen:(UIView *)subView;
+
++ (void)fullScreen:(UIView *)subView edgeInsets:(UIEdgeInsets)edgeInsets;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/ios/IJKMediaDemo/IJKMediaDemo/QHUtilMan/QHViewUtil.m b/ios/IJKMediaDemo/IJKMediaDemo/QHUtilMan/QHViewUtil.m
new file mode 100644
index 00000000..55e7447f
--- /dev/null
+++ b/ios/IJKMediaDemo/IJKMediaDemo/QHUtilMan/QHViewUtil.m
@@ -0,0 +1,41 @@
+//
+// QHViewUtil.m
+// QHChatDemo
+//
+// Created by Anakin chen on 2018/12/21.
+// Copyright © 2018 Chen Network Technology. All rights reserved.
+//
+
+#import "QHViewUtil.h"
+
+@implementation QHViewUtil
+
++ (void)fullScreen:(UIView *)subView {
+ [self fullScreen:subView edgeInsets:UIEdgeInsetsZero];
+}
+
++ (void)fullScreen:(UIView *)subView edgeInsets:(UIEdgeInsets)edgeInsets {
+ if (subView == nil || subView.superview == nil) {
+ return;
+ }
+ subView.translatesAutoresizingMaskIntoConstraints = NO;
+ NSDictionary *viewsDict = NSDictionaryOfVariableBindings(subView);
+ NSString *lcString = [NSString stringWithFormat:@"|-%f-[subView]-%f-|", edgeInsets.left, edgeInsets.right];
+ [subView.superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:lcString options:NSLayoutFormatAlignAllBaseline metrics:0 views:viewsDict]];
+ NSString *lcString2 = [NSString stringWithFormat:@"V:|-%f-[subView]-%f-|", edgeInsets.top, edgeInsets.bottom];
+ [subView.superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:lcString2 options:NSLayoutFormatAlignAllBaseline metrics:0 views:viewsDict]];
+}
+
++ (void)fullScreen2:(UIView *)subView {
+ if (subView == nil || subView.superview == nil) {
+ return;
+ }
+ UIView *superV = subView.superview;
+ subView.translatesAutoresizingMaskIntoConstraints = NO;
+ [superV addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:superV attribute:NSLayoutAttributeTop multiplier:1. constant:0]];
+ [superV addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:superV attribute:NSLayoutAttributeBottom multiplier:1. constant:0]];
+ [superV addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:superV attribute:NSLayoutAttributeLeading multiplier:1. constant:0]];
+ [superV addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:superV attribute:NSLayoutAttributeTrailing multiplier:1. constant:0]];
+}
+
+@end
diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/IJKFFMoviePlayerController.h b/ios/IJKMediaPlayer/IJKMediaPlayer/IJKFFMoviePlayerController.h
index 469c1288..0137e99b 100644
--- a/ios/IJKMediaPlayer/IJKMediaPlayer/IJKFFMoviePlayerController.h
+++ b/ios/IJKMediaPlayer/IJKMediaPlayer/IJKFFMoviePlayerController.h
@@ -135,6 +135,8 @@ typedef enum IJKLogLevel {
#pragma mark KVO properties
@property (nonatomic, readonly) IJKFFMonitor *monitor;
+- (void)addDanmu:(UIView *)view;
+
@end
#define IJK_FF_IO_TYPE_READ (1)
diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/IJKFFMoviePlayerController.m b/ios/IJKMediaPlayer/IJKMediaPlayer/IJKFFMoviePlayerController.m
index 94fcb6ac..a4f4b7ff 100644
--- a/ios/IJKMediaPlayer/IJKMediaPlayer/IJKFFMoviePlayerController.m
+++ b/ios/IJKMediaPlayer/IJKMediaPlayer/IJKFFMoviePlayerController.m
@@ -523,6 +523,10 @@ inline static int getPlayerOption(IJKFFOptionCategory category)
{
}
+- (void)addDanmu:(UIView *)view {
+ [_glView insertSubview:view belowSubview:_glView.showFV];
+}
+
- (IJKMPMoviePlaybackState)playbackState
{
if (!_mediaPlayer)
diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/IJKSDLGLView.h b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/IJKSDLGLView.h
index 66536883..ddac2b4e 100644
--- a/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/IJKSDLGLView.h
+++ b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/IJKSDLGLView.h
@@ -27,7 +27,22 @@
#include "ijksdl/ijksdl_vout.h"
-@interface IJKSDLGLView : UIView
+@protocol IJKSDLGLViewProtocol <NSObject>
+
+- (BOOL)isApplicationActive;
+
+@end
+
+@interface QHIJKSDLGLShowView : UIView
+
+@property(nonatomic, readonly) CGFloat fps;
+@property(nonatomic) CGFloat scaleFactor;
+
+@property (nonatomic, weak) id<IJKSDLGLViewProtocol> protocol;
+
+@end
+
+@interface IJKSDLGLView : UIView <IJKSDLGLViewProtocol>
- (id) initWithFrame:(CGRect)frame;
- (void) display: (SDL_VoutOverlay *) overlay;
@@ -40,4 +55,7 @@
@property(nonatomic) CGFloat scaleFactor;
@property(nonatomic) BOOL shouldShowHudView;
+@property (nonatomic, strong) QHIJKSDLGLShowView *showFV;
+@property (nonatomic, strong) QHIJKSDLGLShowView *showBV;
+
@end
diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/IJKSDLGLView.m b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/IJKSDLGLView.m
index 4e726897..12f3ff27 100644
--- a/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/IJKSDLGLView.m
+++ b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/IJKSDLGLView.m
@@ -35,87 +35,49 @@ typedef NS_ENUM(NSInteger, IJKSDLGLViewApplicationState) {
IJKSDLGLViewApplicationBackgroundState = 2
};
-@interface IJKSDLGLView()
+@interface QHIJKSDLGLShowView ()
@property(atomic,strong) NSRecursiveLock *glActiveLock;
@property(atomic) BOOL glActivePaused;
+@property (nonatomic) BOOL bFront;
+
+@property (nonatomic) EAGLContext *context;
+@property (nonatomic) BOOL didSetupGL;
+@property (nonatomic) BOOL didStopGL;
+@property (nonatomic) BOOL isRenderBufferInvalidated;
@end
-@implementation IJKSDLGLView {
- EAGLContext *_context;
+@implementation QHIJKSDLGLShowView {
GLuint _framebuffer;
GLuint _renderbuffer;
GLint _backingWidth;
GLint _backingHeight;
- int _frameCount;
-
- int64_t _lastFrameTime;
-
IJK_GLES2_Renderer *_renderer;
int _rendererGravity;
-
- BOOL _isRenderBufferInvalidated;
-
- int _tryLockErrorCount;
- BOOL _didSetupGL;
- BOOL _didStopGL;
- BOOL _didLockedDueToMovedToWindow;
- BOOL _shouldLockWhileBeingMovedToWindow;
- NSMutableArray *_registeredNotifications;
-
- IJKSDLHudViewController *_hudViewController;
- IJKSDLGLViewApplicationState _applicationState;
}
+ (Class) layerClass
{
- return [CAEAGLLayer class];
+ return [CAEAGLLayer class];
}
-
-- (id) initWithFrame:(CGRect)frame
+- (id) initWithFrame:(CGRect)frame protocol:(id<IJKSDLGLViewProtocol>)protocol front:(BOOL)bFront
{
self = [super initWithFrame:frame];
if (self) {
- _tryLockErrorCount = 0;
- _shouldLockWhileBeingMovedToWindow = YES;
- self.glActiveLock = [[NSRecursiveLock alloc] init];
- _registeredNotifications = [[NSMutableArray alloc] init];
- [self registerApplicationObservers];
+ self.protocol = protocol;
+ self.bFront = bFront;
_didSetupGL = NO;
- [self setupGLOnce];
-
- _hudViewController = [[IJKSDLHudViewController alloc] init];
- [self addSubview:_hudViewController.tableView];
}
return self;
}
-- (void)willMoveToWindow:(UIWindow *)newWindow
-{
- if (!_shouldLockWhileBeingMovedToWindow) {
- [super willMoveToWindow:newWindow];
- return;
- }
- if (newWindow && !_didLockedDueToMovedToWindow) {
- [self lockGLActive];
- _didLockedDueToMovedToWindow = YES;
- }
- [super willMoveToWindow:newWindow];
-}
-
-- (void)didMoveToWindow
-{
- [super didMoveToWindow];
- if (self.window && _didLockedDueToMovedToWindow) {
- [self unlockGLActive];
- _didLockedDueToMovedToWindow = NO;
- }
-}
-
- (BOOL)setupEAGLContext:(EAGLContext *)context
{
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_SRC_ALPHA);
+
glGenFramebuffers(1, &_framebuffer);
glGenRenderbuffers(1, &_renderbuffer);
glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
@@ -159,6 +121,7 @@ typedef NS_ENUM(NSInteger, IJKSDLGLViewApplicationState) {
[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking,
kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,
nil];
+ self.backgroundColor = [UIColor clearColor];
_scaleFactor = [[UIScreen mainScreen] scale];
if (_scaleFactor < 0.1f)
@@ -185,47 +148,13 @@ typedef NS_ENUM(NSInteger, IJKSDLGLViewApplicationState) {
return _didSetupGL;
}
-- (BOOL)setupGLOnce
-{
- if (_didSetupGL)
- return YES;
-
- if ([self isApplicationActive] == NO)
- return NO;
-
- if (![self tryLockGLActive])
- return NO;
-
- BOOL didSetupGL = [self setupGL];
- [self unlockGLActive];
- return didSetupGL;
-}
-
- (BOOL)isApplicationActive
{
- switch (_applicationState) {
- case IJKSDLGLViewApplicationForegroundState:
- return YES;
- case IJKSDLGLViewApplicationBackgroundState:
- return NO;
- default: {
- UIApplicationState appState = [UIApplication sharedApplication].applicationState;
- switch (appState) {
- case UIApplicationStateActive:
- return YES;
- case UIApplicationStateInactive:
- case UIApplicationStateBackground:
- default:
- return NO;
- }
- }
- }
+ return [self.protocol isApplicationActive];
}
-- (void)dealloc
+- (void)m_dealloc
{
- [self lockGLActive];
-
_didStopGL = YES;
EAGLContext *prevContext = [EAGLContext currentContext];
@@ -249,33 +178,16 @@ typedef NS_ENUM(NSInteger, IJKSDLGLViewApplicationState) {
[EAGLContext setCurrentContext:prevContext];
_context = nil;
-
- [self unregisterApplicationObservers];
-
- [self unlockGLActive];
}
- (void)setScaleFactor:(CGFloat)scaleFactor
{
_scaleFactor = scaleFactor;
- [self invalidateRenderBuffer];
}
- (void)layoutSubviews
{
[super layoutSubviews];
-
- CGRect selfFrame = self.frame;
- CGRect newFrame = selfFrame;
-
- newFrame.size.width = selfFrame.size.width * 1 / 3;
- newFrame.origin.x = selfFrame.size.width * 2 / 3;
-
- newFrame.size.height = selfFrame.size.height * 8 / 8;
- newFrame.origin.y += selfFrame.size.height * 0 / 8;
-
- _hudViewController.tableView.frame = newFrame;
- [self invalidateRenderBuffer];
}
- (void)setContentMode:(UIViewContentMode)contentMode
@@ -296,7 +208,6 @@ typedef NS_ENUM(NSInteger, IJKSDLGLViewApplicationState) {
_rendererGravity = IJK_GLES2_GRAVITY_RESIZE_ASPECT;
break;
}
- [self invalidateRenderBuffer];
}
- (BOOL)setupRenderer: (SDL_VoutOverlay *) overlay
@@ -310,7 +221,7 @@ typedef NS_ENUM(NSInteger, IJKSDLGLViewApplicationState) {
IJK_GLES2_Renderer_reset(_renderer);
IJK_GLES2_Renderer_freeP(&_renderer);
- _renderer = IJK_GLES2_Renderer_create(overlay);
+ _renderer = QH_IJK_GLES2_Renderer_create_for(overlay, self.bFront);
if (!IJK_GLES2_Renderer_isValid(_renderer))
return NO;
@@ -323,52 +234,22 @@ typedef NS_ENUM(NSInteger, IJKSDLGLViewApplicationState) {
return YES;
}
-- (void)invalidateRenderBuffer
-{
- NSLog(@"invalidateRenderBuffer\n");
- [self lockGLActive];
-
- _isRenderBufferInvalidated = YES;
-
- if ([[NSThread currentThread] isMainThread]) {
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
- if (_isRenderBufferInvalidated)
- [self display:nil];
- });
- } else {
- [self display:nil];
- }
-
- [self unlockGLActive];
-}
-
- (void)display: (SDL_VoutOverlay *) overlay
{
- if (![self setupGLOnce])
- return;
-
- if (![self tryLockGLActive]) {
- if (0 == (_tryLockErrorCount % 100)) {
- NSLog(@"IJKSDLGLView:display: unable to tryLock GL active: %d\n", _tryLockErrorCount);
- }
- _tryLockErrorCount++;
- return;
- }
-
- _tryLockErrorCount = 0;
if (_context && !_didStopGL) {
EAGLContext *prevContext = [EAGLContext currentContext];
[EAGLContext setCurrentContext:_context];
[self displayInternal:overlay];
[EAGLContext setCurrentContext:prevContext];
}
-
- [self unlockGLActive];
}
// NOTE: overlay could be NULl
- (void)displayInternal: (SDL_VoutOverlay *) overlay
{
+ if (_context == NULL) {
+ return;
+ }
if (![self setupRenderer:overlay]) {
if (!overlay && !_renderer) {
NSLog(@"IJKSDLGLView: setupDisplay not ready\n");
@@ -399,7 +280,308 @@ typedef NS_ENUM(NSInteger, IJKSDLGLViewApplicationState) {
glBindRenderbuffer(GL_RENDERBUFFER, _renderbuffer);
[_context presentRenderbuffer:GL_RENDERBUFFER];
+}
+
+#pragma mark AppDelegate
+
+- (void)toggleGLPaused:(BOOL)paused
+{
+ if (_context != nil) {
+ EAGLContext *prevContext = [EAGLContext currentContext];
+ [EAGLContext setCurrentContext:_context];
+ glFinish();
+ [EAGLContext setCurrentContext:prevContext];
+ }
+}
+
+#pragma mark snapshot
+
+- (UIImage*)snapshotInternal
+{
+ if (isIOS7OrLater()) {
+ return [self snapshotInternalOnIOS7AndLater];
+ } else {
+ return [self snapshotInternalOnIOS6AndBefore];
+ }
+}
+
+- (UIImage*)snapshotInternalOnIOS7AndLater
+{
+ if (CGSizeEqualToSize(self.bounds.size, CGSizeZero)) {
+ return nil;
+ }
+ UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0);
+ // Render our snapshot into the image context
+ [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:NO];
+
+ // Grab the image from the context
+ UIImage *complexViewImage = UIGraphicsGetImageFromCurrentImageContext();
+ // Finish using the context
+ UIGraphicsEndImageContext();
+
+ return complexViewImage;
+}
+
+- (UIImage*)snapshotInternalOnIOS6AndBefore
+{
+ EAGLContext *prevContext = [EAGLContext currentContext];
+ [EAGLContext setCurrentContext:_context];
+
+ GLint backingWidth, backingHeight;
+
+ // Bind the color renderbuffer used to render the OpenGL ES view
+ // If your application only creates a single color renderbuffer which is already bound at this point,
+ // this call is redundant, but it is needed if you're dealing with multiple renderbuffers.
+ // Note, replace "viewRenderbuffer" with the actual name of the renderbuffer object defined in your class.
+ glBindRenderbuffer(GL_RENDERBUFFER, _renderbuffer);
+
+ // Get the size of the backing CAEAGLLayer
+ glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
+ glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
+
+ NSInteger x = 0, y = 0, width = backingWidth, height = backingHeight;
+ NSInteger dataLength = width * height * 4;
+ GLubyte *data = (GLubyte*)malloc(dataLength * sizeof(GLubyte));
+
+ // Read pixel data from the framebuffer
+ glPixelStorei(GL_PACK_ALIGNMENT, 4);
+ glReadPixels((int)x, (int)y, (int)width, (int)height, GL_RGBA, GL_UNSIGNED_BYTE, data);
+
+ // Create a CGImage with the pixel data
+ // If your OpenGL ES content is opaque, use kCGImageAlphaNoneSkipLast to ignore the alpha channel
+ // otherwise, use kCGImageAlphaPremultipliedLast
+ CGDataProviderRef ref = CGDataProviderCreateWithData(NULL, data, dataLength, NULL);
+ CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
+ CGImageRef iref = CGImageCreate(width, height, 8, 32, width * 4, colorspace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast,
+ ref, NULL, true, kCGRenderingIntentDefault);
+
+ [EAGLContext setCurrentContext:prevContext];
+
+ // OpenGL ES measures data in PIXELS
+ // Create a graphics context with the target size measured in POINTS
+ UIGraphicsBeginImageContext(CGSizeMake(width, height));
+
+ CGContextRef cgcontext = UIGraphicsGetCurrentContext();
+ // UIKit coordinate system is upside down to GL/Quartz coordinate system
+ // Flip the CGImage by rendering it to the flipped bitmap context
+ // The size of the destination area is measured in POINTS
+ CGContextSetBlendMode(cgcontext, kCGBlendModeCopy);
+ CGContextDrawImage(cgcontext, CGRectMake(0.0, 0.0, width, height), iref);
+
+ // Retrieve the UIImage from the current context
+ UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+
+ // Clean up
+ free(data);
+ CFRelease(ref);
+ CFRelease(colorspace);
+ CGImageRelease(iref);
+
+ return image;
+}
+
+@end
+
+@interface IJKSDLGLView()
+@property(atomic,strong) NSRecursiveLock *glActiveLock;
+@property(atomic) BOOL glActivePaused;
+@end
+
+@implementation IJKSDLGLView {
+ int _frameCount;
+
+ int64_t _lastFrameTime;
+
+ int _tryLockErrorCount;
+
+ BOOL _didLockedDueToMovedToWindow;
+ BOOL _shouldLockWhileBeingMovedToWindow;
+ NSMutableArray *_registeredNotifications;
+
+ IJKSDLHudViewController *_hudViewController;
+ IJKSDLGLViewApplicationState _applicationState;
+}
+
++ (Class) layerClass
+{
+ return [CAEAGLLayer class];
+}
+
+- (id) initWithFrame:(CGRect)frame
+{
+ self = [super initWithFrame:frame];
+ if (self) {
+ _tryLockErrorCount = 0;
+ _shouldLockWhileBeingMovedToWindow = YES;
+ self.glActiveLock = [[NSRecursiveLock alloc] init];
+ _registeredNotifications = [[NSMutableArray alloc] init];
+ [self registerApplicationObservers];
+
+ QHIJKSDLGLShowView *showBV = [[QHIJKSDLGLShowView alloc] initWithFrame:frame protocol:self front:false];
+ [self addSubview:showBV];
+ _showBV = showBV;
+ QHIJKSDLGLShowView *showFV = [[QHIJKSDLGLShowView alloc] initWithFrame:frame protocol:self front:true];
+ [self addSubview:showFV];
+ _showFV = showFV;
+
+ [self setupGLOnce];
+
+ _hudViewController = [[IJKSDLHudViewController alloc] init];
+ [self addSubview:_hudViewController.tableView];
+ }
+
+ return self;
+}
+
+- (void)willMoveToWindow:(UIWindow *)newWindow
+{
+ if (!_shouldLockWhileBeingMovedToWindow) {
+ [super willMoveToWindow:newWindow];
+ return;
+ }
+ if (newWindow && !_didLockedDueToMovedToWindow) {
+ [self lockGLActive];
+ _didLockedDueToMovedToWindow = YES;
+ }
+ [super willMoveToWindow:newWindow];
+}
+
+- (void)didMoveToWindow
+{
+ [super didMoveToWindow];
+ if (self.window && _didLockedDueToMovedToWindow) {
+ [self unlockGLActive];
+ _didLockedDueToMovedToWindow = NO;
+ }
+}
+
+- (BOOL)setupGLOnce
+{
+ if (self.showBV.didSetupGL && self.showFV.didSetupGL)
+ return YES;
+
+ if ([self isApplicationActive] == NO)
+ return NO;
+
+ if (![self tryLockGLActive])
+ return NO;
+
+ BOOL didSetupGL = [self.showBV setupGL] && [self.showFV setupGL];
+ [self unlockGLActive];
+ return didSetupGL;
+}
+
+- (BOOL)isApplicationActive
+{
+ switch (_applicationState) {
+ case IJKSDLGLViewApplicationForegroundState:
+ return YES;
+ case IJKSDLGLViewApplicationBackgroundState:
+ return NO;
+ default: {
+ UIApplicationState appState = [UIApplication sharedApplication].applicationState;
+ switch (appState) {
+ case UIApplicationStateActive:
+ return YES;
+ case UIApplicationStateInactive:
+ case UIApplicationStateBackground:
+ default:
+ return NO;
+ }
+ }
+ }
+}
+
+- (void)dealloc
+{
+ [self lockGLActive];
+
+ [self.showBV m_dealloc];
+ [self.showFV m_dealloc];
+
+ [self unregisterApplicationObservers];
+
+ [self unlockGLActive];
+}
+
+- (void)setScaleFactor:(CGFloat)scaleFactor
+{
+ [self.showBV setScaleFactor:scaleFactor];
+ [self.showFV setScaleFactor:scaleFactor];
+ [self invalidateRenderBuffer];
+}
+
+- (void)layoutSubviews
+{
+ [super layoutSubviews];
+
+ CGRect selfFrame = self.frame;
+ CGRect newFrame = selfFrame;
+
+ newFrame.size.width = selfFrame.size.width * 1 / 3;
+ newFrame.origin.x = selfFrame.size.width * 2 / 3;
+
+ newFrame.size.height = selfFrame.size.height * 8 / 8;
+ newFrame.origin.y += selfFrame.size.height * 0 / 8;
+
+ _hudViewController.tableView.frame = newFrame;
+ [self invalidateRenderBuffer];
+}
+
+- (void)setContentMode:(UIViewContentMode)contentMode
+{
+ [super setContentMode:contentMode];
+ [self.showBV setContentMode:contentMode];
+ [self.showFV setContentMode:contentMode];
+
+ [self invalidateRenderBuffer];
+}
+
+- (void)invalidateRenderBuffer
+{
+ NSLog(@"invalidateRenderBuffer\n");
+ [self lockGLActive];
+
+ self.showBV.isRenderBufferInvalidated = YES;
+
+ if ([[NSThread currentThread] isMainThread]) {
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
+ if (self.showBV.isRenderBufferInvalidated)
+ [self display:nil];
+ });
+ } else {
+ [self display:nil];
+ }
+
+ [self unlockGLActive];
+}
+
+- (void)display: (SDL_VoutOverlay *) overlay
+{
+ if (![self setupGLOnce])
+ return;
+
+ if (![self tryLockGLActive]) {
+ if (0 == (_tryLockErrorCount % 100)) {
+ NSLog(@"IJKSDLGLView:display: unable to tryLock GL active: %d\n", _tryLockErrorCount);
+ }
+ _tryLockErrorCount++;
+ return;
+ }
+
+ _tryLockErrorCount = 0;
+ [self.showBV display:overlay];
+ [self.showFV display:overlay];
+ [self displayInternal:overlay];
+
+ [self unlockGLActive];
+}
+
+// NOTE: overlay could be NULl
+- (void)displayInternal: (SDL_VoutOverlay *) overlay
+{
int64_t current = (int64_t)SDL_GetTickHR();
int64_t delta = (current > _lastFrameTime) ? current - _lastFrameTime : 0;
if (delta <= 0) {
@@ -450,12 +632,8 @@ typedef NS_ENUM(NSInteger, IJKSDLGLViewApplicationState) {
{
[self lockGLActive];
if (!self.glActivePaused && paused) {
- if (_context != nil) {
- EAGLContext *prevContext = [EAGLContext currentContext];
- [EAGLContext setCurrentContext:_context];
- glFinish();
- [EAGLContext setCurrentContext:prevContext];
- }
+ [self.showBV toggleGLPaused:paused];
+ [self.showFV toggleGLPaused:paused];
}
self.glActivePaused = paused;
[self unlockGLActive];
@@ -551,87 +729,7 @@ typedef NS_ENUM(NSInteger, IJKSDLGLViewApplicationState) {
- (UIImage*)snapshotInternal
{
- if (isIOS7OrLater()) {
- return [self snapshotInternalOnIOS7AndLater];
- } else {
- return [self snapshotInternalOnIOS6AndBefore];
- }
-}
-
-- (UIImage*)snapshotInternalOnIOS7AndLater
-{
- if (CGSizeEqualToSize(self.bounds.size, CGSizeZero)) {
- return nil;
- }
- UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0);
- // Render our snapshot into the image context
- [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:NO];
-
- // Grab the image from the context
- UIImage *complexViewImage = UIGraphicsGetImageFromCurrentImageContext();
- // Finish using the context
- UIGraphicsEndImageContext();
-
- return complexViewImage;
-}
-
-- (UIImage*)snapshotInternalOnIOS6AndBefore
-{
- EAGLContext *prevContext = [EAGLContext currentContext];
- [EAGLContext setCurrentContext:_context];
-
- GLint backingWidth, backingHeight;
-
- // Bind the color renderbuffer used to render the OpenGL ES view
- // If your application only creates a single color renderbuffer which is already bound at this point,
- // this call is redundant, but it is needed if you're dealing with multiple renderbuffers.
- // Note, replace "viewRenderbuffer" with the actual name of the renderbuffer object defined in your class.
- glBindRenderbuffer(GL_RENDERBUFFER, _renderbuffer);
-
- // Get the size of the backing CAEAGLLayer
- glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
- glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
-
- NSInteger x = 0, y = 0, width = backingWidth, height = backingHeight;
- NSInteger dataLength = width * height * 4;
- GLubyte *data = (GLubyte*)malloc(dataLength * sizeof(GLubyte));
-
- // Read pixel data from the framebuffer
- glPixelStorei(GL_PACK_ALIGNMENT, 4);
- glReadPixels((int)x, (int)y, (int)width, (int)height, GL_RGBA, GL_UNSIGNED_BYTE, data);
-
- // Create a CGImage with the pixel data
- // If your OpenGL ES content is opaque, use kCGImageAlphaNoneSkipLast to ignore the alpha channel
- // otherwise, use kCGImageAlphaPremultipliedLast
- CGDataProviderRef ref = CGDataProviderCreateWithData(NULL, data, dataLength, NULL);
- CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
- CGImageRef iref = CGImageCreate(width, height, 8, 32, width * 4, colorspace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast,
- ref, NULL, true, kCGRenderingIntentDefault);
-
- [EAGLContext setCurrentContext:prevContext];
-
- // OpenGL ES measures data in PIXELS
- // Create a graphics context with the target size measured in POINTS
- UIGraphicsBeginImageContext(CGSizeMake(width, height));
-
- CGContextRef cgcontext = UIGraphicsGetCurrentContext();
- // UIKit coordinate system is upside down to GL/Quartz coordinate system
- // Flip the CGImage by rendering it to the flipped bitmap context
- // The size of the destination area is measured in POINTS
- CGContextSetBlendMode(cgcontext, kCGBlendModeCopy);
- CGContextDrawImage(cgcontext, CGRectMake(0.0, 0.0, width, height), iref);
-
- // Retrieve the UIImage from the current context
- UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
- UIGraphicsEndImageContext();
-
- // Clean up
- free(data);
- CFRelease(ref);
- CFRelease(colorspace);
- CGImageRelease(iref);
-
- return image;
+ return [self.showBV snapshotInternal];
}
#pragma mark IJKFFHudController
diff --git a/ios/compile-ffmpeg.sh b/ios/compile-ffmpeg.sh
index c98ec50c..d50f5737 100755
--- a/ios/compile-ffmpeg.sh
+++ b/ios/compile-ffmpeg.sh
@@ -19,9 +19,9 @@
#----------
# modify for your build tool
-FF_ALL_ARCHS_IOS6_SDK="armv7 armv7s i386"
-FF_ALL_ARCHS_IOS7_SDK="armv7 armv7s arm64 i386 x86_64"
-FF_ALL_ARCHS_IOS8_SDK="armv7 arm64 i386 x86_64"
+FF_ALL_ARCHS_IOS6_SDK="armv7s i386"
+FF_ALL_ARCHS_IOS7_SDK="armv7s arm64 i386 x86_64"
+FF_ALL_ARCHS_IOS8_SDK="arm64 i386 x86_64"
FF_ALL_ARCHS=$FF_ALL_ARCHS_IOS8_SDK
--
2.31.1
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/chenqihui/qhaidanmu-man.git
git@gitee.com:chenqihui/qhaidanmu-man.git
chenqihui
qhaidanmu-man
QHAIDanmuMan
master

搜索帮助