BAT大厂程序员常用的IDE工具,你接触过哪些?

Cloud Toolkit 是一款 IDE 插件,可以帮助开发者更高效地开发、测试、诊断并部署应用。通过 Cloud Toolkit,开发者能够方便地将本地应用一键部署到任意机器(本地或云端),并内置 Arthas 诊断、高效执行终端命令和 SQL 等,提供 IntelliJ IDEA 版,Eclipse 版,PyCharm 版和 Maven 版。

工具的使用场景:

  • 每次修改完代码后,是否正在经历反复地打包?
  • 在 Maven 、Git 以及其他运维脚本和工具的之间频繁切换?
  • 采用 SCP 工具上传?使用 XShell 或 SecureCRT 登录服务器?替换部署包?重启?
  • 文件上传到服务器指定目录,在各种 FTP、SCP 工具之间频繁切换 ?

工具地址:https://www.aliyun.com/product/cloudtoolkit

说起Jetbrains这家公司,大家一定不陌生,如果陌生那你也一定用过他家的IDE工具集,比如,以下的产品。

最有名气的就是IDEA这个万能IDE了,当然一般也就是学习编写Java程序时使用,jetbrains的IDE工具最具有的特色便是智慧了,代码提示,代码补全,以及数以万计的插件、主题等。

不管是什么语言,目前常用的语言IDE都有它的一席之地。

Jetbrains各类工具的作用范围

  • RM->RubyMine是一款针对于Ruby语言的IDE工具
  • PC->PyCharm是一款面向专业的Python开发者的IDE工具
  • IJ->IntelliJ IDEA是一款功能强大,符合人体工程学的 JVM IDE,一般用于Java语言的开发
  • PS->PhpStorm是一款高效智能的PHP开发工具
  • GO->GoLand 使读取、写入和更改 Go 代码变得非常容易
  • RD->JetBrains Rider 是一款基于 IntelliJ 平台和 ReSharper 的跨平台 .NET IDE
  • AC->AppCode适用于 iOS/macOS 开发的智能 IDE
  • WS->WebStorm 是一个适用于 JavaScript 和相关技术的集成开发环境
  • CL->CLion是一款智能的 C 和 C++ 编辑器,也是一款跨平台的 IDE工具
  • DG->DataGrip是一个数据库管理工具,基本上支持市面上所以数据库环境的连接

R#->ReSharper是一款适用于.NET开发者的Visual Studio扩展 同时Jetbrains的工具与扩展还远远不止这些,甚至还有自我开发的新型语言->kotlin,可以说是浓缩版的Java

OpenSumi 是一款面向垂直领域,低门槛、高性能、高定制性的双端(Web 及 Electron)IDE 研发的框架。

框架早期由阿里集团淘系工程团队及蚂蚁集团体验技术部、研发效能团队联合发起,共同研发的 IDE 标准化研发框架。它基于 TypeScript + React 进行编码,实现了包含资源管理器、编辑器、调试、Git 面板、搜索面板等核心功能模块,开发者只要基于我们的起步项目进行简单配置,便可以快速地搭建属于自己的本地或云端 IDE 产品,框架自身兼容 VS Code 插件生态,主流 VS Code 插件均可无缝在基于 OpenSumi 研发的产品中运行,同时,框架也为开发者提供多种低成本,高定制的视图定制能力,能满足 IDE 场景下绝大多数的视图定制场景。

针对小程序研发场景, 支付宝小程序开发者工具 以及 淘宝小程序开发者工具 便是使用了 OpenSumi 作为核心框架进行实现。

Atom 是由 GitHub 的程序员们打造的称为“属于21世纪”的代码编辑器。它开源免费跨平台(支持 Windows、Mac、Linux 三大桌面平台),并且整合 GIT 并提供类似 SublimeText 的包管理功能,作为一个现代的代码编辑器,Atom 支持各种编程语言的代码高亮(HTML / CSS / Javascript / PHP / Python / C / C++ / Objective C / Java / JSON / Perl / CoffeeScript / Go / Sass / YAML / Markdown 等等)、 与大多数其他编辑器相比,Atom 的语言支持已经算是覆盖非常全面了。

另外,它的代码补全功能(也叫Snippets) 也非常好用,你只需输入几个字符即可展开成各种常用代码,可以极大提高编程效率。

官方地址:https://atom.io/

FinClip 推出的小程序 IDE 工具,界面与微信小程序的开发工具类似

FinClip 天然支持微信小程序语法 WXML,就算你是开发微信小程序也可以用这个 IDE 进行开发和调试,导出代码包后可以实现微信小程序平台和 FinClip 小程序平台的同时上线。

与此同时,它还支持「「小程序一键转换成 APP」」,可以将已有小程序代码导出为 IOS 与 Android 中可用的工程文件,由于导出的工程文件已经集成了 FinClip SDK ,所以生成的这个APP直接拥有小程序的运行能力,后续可在这个 APP 上继续上架更多小程序,自建自己的小程序生态。

工具地址:https://www.finclip.com/downloads/

NetBeans 是 Java 的集成开发环境。这是一个了不起的IDE,用户评分为4.1分(满分5分),用户满意度高达82%。它可以在Windows,Linux,macOS和Solaris上运行。它具有内置工具,可为从产品设计到部署的整个软件开发生命周期增加价值。NetBeans 的一些主要功能包括:

  • 它可以检测错误并为您提供智能代码编译功能。
  • 通过 NetBeans,您可以直接创建、调试、部署和测试应用程序。
  • 它具有非常简单易用的管理功能。
  • 它具有惊人的代码比较功能,可帮助您同时编写类似的代码 。

那你常用的 IDE 工具是什么呢?或者有啥好的IDE工具推荐呢?请在下面的评论栏里告诉我吧~

Java 中的 CAS 原理解析

图片来自互联网

CAS是乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。

CAS(Compare And Swap)意为比较并且交换,CAS它是一个原子操作。CAS操作涉及到三个值:当前内存中的值V,逾期内存中的值E和待更新的值U。如果当前内存中的值V等于预期值E,则将内存中的值更新为U,CAS操作成功。否则不更新CAS操作失败。

悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中的synchronized锁和ReentrantLock都是悲观的锁。

乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

Java和C++语言的一个重要区别就是Java中我们无法直接操作一块内存区域,不能像C++中那样可以自己申请内存和释放内存。Java中的Unsafe类为我们提供了类似C++手动管理内存的能力。Unsafe类,全限定名是sun.misc.Unsafe,从名字中我们可以看出来这个类对普通程序员来说是“危险”的,一般应用开发者不会用到这个类。

Unsafe提供的CAS操作:

CAS在Java中有很多应用,JUC以及java.util.concurrent.atomic下面的原子类都用到了CAS。

Unsafe为Java提供了很多底层功能,其中Java中的CAS功能就是通过这个类来实现的。

Unsafe中提供了Object,int和long类型的CAS操作。其他类型的需要自己实现。CAS它是一个原子操作。要保证线程安全性,除了原子性,还有可见性和有序性。可见性和有序性在Java中都可以通过volatile来实现。

Java中的原子类(java.util.concurrent.atomic)

java.util.concurrent.atomic包中的类通过volatile+CAS重试保证线程安全性。java.util.concurrent.atomic包下面的原子类可以分为四种类型:

我们主要分析一下AtomicInteger这个类如何保证线程安全性:

AtomicInteger的value是volatile的

AtomicInteger中的value是volatile的,volatile可以保证可见性和有序性。

可以看到AtomicInteger的get操作是不加锁的,对于非volatile类型的共享变量,并发操作时,一个读线程未必能立马读取到其他线程对这个共享变量的修改。但是这里的value是volatile的,因此可以立马看到其他线程对value的修改。

incrementAndGet操作

incrementAndGet操作会先将value加1,然后返回新的值。这个方法内部会调用Unsafe的getAndAddInt方法:

可以看到Unsafe中在循环体内先读取内存中的value值,然后CAS更新,如果CAS更新成功则退出,如果更新失败,则循环重试直到更新成功。

在前面的文章中(Java中的Unsafe)我们分析了Unsafe中提供了三种类型对象的CAS操作:Object,int和long类型。AtomicLong是通过Unsafe提供的long类型的CAS操作实现的,AtomicReference是通过Unsafe提供的Object类型的CAS操作实现的。

自旋锁+CAS如果长时间不成功,会给CPU带来非常大的执行开销。如果JVM能支持处理器提供的pause指令那么效率会有一定的提升,pause指令有两个作用,第一它可以延迟流水线执行指令(de-pipeline),使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零。第二它可以避免在退出循环的时候因内存顺序冲突(memory order violation)而引起CPU流水线被清空(CPU pipeline flush),从而提高CPU的执行效率。

java.util.concurrent.atomic通过基于CAS的乐观锁保证线程安全性。在多读少写的场景下,较synchronized锁和ReentrantLock的悲观锁性能会更好。

因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。AtomicStampedReference类具有版本号功能。

当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁,或者有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。比如有两个共享变量i=2,j=a,合并一下ij=2a,然后用CAS来操作ij。从Java1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行CAS操作。

超详细讲解IJKPlayer的播放器实战和源码分析(1)

0.引言

关于本篇文章的学习,一定要先学习ffplay源码,对ffplay源码的整个流程要理解,才能够理解本篇文章,那就需要参考前面的文章。文章列表如下:

详细介绍ffplay命令(1)

FFmpeg的FFplay框架分析

超详细解析FFplay之音视频同步

超详细解析FFplay之音视频控制

超详细解析FFplay之数据读取线程

FFplay超详细数据结构分析

超详细解析FFplay之音视频SEEK操作

超详细解析FFplay之音视频解码线程

超详细解析FFplay之视频输出和尺寸变换模块

超详细解析FFplay之音视频输出模块

注意:本篇文章篇幅非常长,阅读起来需要花一些时间,接下来就开始认真学习IJKPlayer吧。

1.ijkplayer简述

本篇文章主要讲解ijkplayer重要源码分析(拉取的是最新的源码)和如何移植源码到qt的方法。ijkplayer是一个基于FFPlay源码的轻量级Android/iOS视频播放器,实现了跨平台的功能,API易于集成;编译配置可裁剪,⽅便控制安装包大小。接口和结构会直接借鉴IJKPlayer和ffplay。IJKPlayer和ffplay接口都是可以做到商用,可以使用这2种接口快速开发,如果做音视频的人很少,那可以直接基于这些接口开发。达到一个ijkplayer的效果。

ijkplayer源码地址:

https://gitee.com/mirrors/ijkplayer

界面如下:

2.ijkplayer目录结构

在功能的具体实现上,iOS和Android平台的差异主要表现在视频硬件解码以及⾳视频渲染⽅⾯,两者实现的载体区别如下图所示:

ijkplayer源码主要由andoid、config、doc、extra、ijkmedia、ijkprof、ios、tools、xxx.sh、,ijkplayer源码的目录结构如下:

(1)android目录:android平台相关的上层接口封装以及平台相关方法,里面还有各种编译脚本相关,不同指令集的源码版本,如v7和v8等,还有一些patch相关的记录。具体细节部分,可以自行下载源码,然后阅读。

编译脚本:

(2)config目录:主要是编译ffmpeg使用的配置文件,如编译什么模块,如何编译HEVC等。如下图:

(3)extra目录:存放编译ijkplayer所需的依赖源文件,如ffmpeg、openssl、libyuv等。

(4)ijkmedia目录:这里面就是关于底层源码,包括jni,ffplay的源码。

(5)ijkprof目录:这个目录里面不太重要,内容不是很多。

(6)ios目录:ios平台上的上层接口封装及平台相关方法,同时也有一些编译脚本。

(7)tools:表示初始化项目工程脚本。

注意:上面目录的脚本也很多,每个脚本都有相应的功能,这些在做SDK时,也是值得我们参考和学习。

3.整体播放流程

read_thread线程负责解复用,,video_thread负责视频解码,audio_thread负责音频解码,ffplay的控制和显示是在一个线程,自己设计的这个播放器,控制和显示就不在同一个线程。控制就是在UI里面的子线程,如video_refresh_thread。

(1)把ijk的源码建立一个srcinsight的工程,可以很明显看到,ijk就是基于ffplay(特别是有些结构体,如packet队列,frame队列,都是照搬ffplay)做的优化和修改,在ff_ffplay_def.f里的结构体,下面这个FFPlayer的结构体是ijk重新又封装了,如下:

(2)Packet队列数据结构如下。

(3)Frame队列数据结构如下。

注意:如果不懂前面ffplay的,可以看看前面的文章,这是理解ijk的基础。

(4)在ijk源码中,ff_ffplay.h是总体的一个头文件和对外提供接口的头文件。

(5)播放前设置参数的接口

(6)播放控制

(7)ff_ffmsg.h主要是一些回调信息,及时反馈的一些错误码信息。如下图:

4.移植重要源码到QT平台

添加顺序依次为

ff_ffplay_def.h

ff_fferror.h

ff_ffmsg.h

ff_ffplay.h:主要是对外提供接口。

(1)添加如下头文件

在qt项目下,新建头文件,

(2)创建目录在src下,名字为ff_ffplay_def.h,如下图所示:

并在ff_ffplay_def.h下添加如下的头文件,这些头文件也主要是来源于ffplay.c,添加如下:

(3)添加ff_fferror.h,如下:

(4)包含头文件

(5)结构体IjkMediaPlayer包含了FFPlayer结构体,代码如下图所示:

(6)ijkplayer主要在移动端的解决方案,调用层次由java(是一个控件,显示画面,暂停,播放等,主要是业务相关)->ijkplayer_jni.c(jni)->ijkplayer.c->ff_ffplay.c。

(7)创建文件,qt接口,通过信号槽去触发,面向接口去编程,保证底层的ffplay.c的实现层不变。命名为ijkplayer_qt.cpp和ijkplayer_qt.h。这边就需要添加上ijkplayer.h、ijkplayer.c、ff_ffplay.c。

创建一个类,命名为ijkplayer_qt,如下图:

(8)在ijkplayer_qt.h添加如下源码:

ijkplayer_qt.cpp添加如下源码:

注意:现在主要是把架子搭起来。

(9)创建ijkplayer.h,如下图所示:

创建ijkplayer.cpp,如下图所示:

(10)创建ff_ffplay.c,如下图所示:

先实现一些初始化相关的工作,如下图所示:

在ff_ffplay.c里做的一些工作,如下图所示:

(11)添加消息队列接口ff_ffmsg.h,如下:

(12)添加config文件,如下:

(13)添加ff_ffinc.h文件,如下:

(14)消息队列的设计

qt播放按钮->IjkPlayerQt->IjkPlayer.cpp->ff_ffplay.c

创建一个结构体IjkMediaPlayer,这个结构体到时候要放在IjkPlayerQt使用。该结构体里面会包含FFPlayer,这样一种关联关系。同样要像IJK源码一样,实现一个loop的效果。

消息队列

初始化Init函数,创建player

信号槽

开启队列

设置资源

创建文件ijkplayer_internal.h。如下界面:

第二版编译完成。暂时没有报错。

5.Android初始化流程

播放的步骤:

设置播放源:ijkmp_set_data_source

启动播放:ijkmp_prepare_async

(1)创建播放器对象。函数IjkMediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)调用ijkmp_android_create(message_loop),message_loop作为回调函数被传入。代码如下图所示:

(2)函数static int message_loop(void *arg)调用函数message_loop_n(env, mp),代码如下图所示:

(3)ijkplayer_jni.c(jni)在这里有个循环控制入口,由这个函数进去。代码如下图所示:

(4)函数message_loop_n(JNIEnv *env, IjkMediaPlayer *mp)调用这个函数ijkmp_get_msg(mp, &msg, 1)(涉及到消息队列这块)是非常重要。

6.播放流程

函数ijkmp_set_data_source从IDLE到INTIALIZED只是设置一个播放的url。函数ijkmp_prepare_async,从INTIALIZED到ASYNC_PREPING,是一个异步操作,做一些播放器的初始化工作。然后就到PREPARED状态,这时候表示初始化工作完成,然后调用ijkmp_start,到STARTED状态。播放流程的状态机如下图所示:

(1)播放开始流程,IjkMediaPlayer_prepareAsync(JNIEnv *env, jobject thiz)->ijkmp_prepare_async(mp)->ijkmp_prepare_async_l(mp)->ffp_prepare_async_l(mp->ffplayer, mp->data_source)

(2)播放接口

(3)播放正真对接ffplay

(4)通过这个接口,可以找到ffplay的函数了

7.暂停流程

(1)函数IjkMediaPlayer_pause(JNIEnv *env, jobject thiz)->调用ijkmp_pause(mp)->ijkmp_pause_l(mp)->回调ffp_notify_msg1(mp->ffplayer, FFP_REQ_PAUSE)->msg_queue_put_simple3(&ffp->msg_queue, what, 0, 0)->msg_queue_put(q, &msg)->msg_queue_put_private(q, msg)->

(2)

(3)

(4)如这个暂停状态来说,函数ffp_pause_l(mp->ffplayer)->toggle_pause(ffp, 1),就到了FFplaye的源码,就会去调用这些关系。

(5)函数toggle_pause在ff_ffplay.c,源码如下。

(6)

暂停成功后,就会去调用函数ijkmp_change_state_l(mp, MP_STATE_PAUSED),去修改暂停状态。这个时候如果需要在java层去显示,那就需要反馈给java层去显示或通知用户。

8.消息通知

(1)使用消息通知的方式,做出相应的操作。

(2)将消息放到消息队列里面去。

(3)

(4)使用链表把消息串起来,并使用信号量SDL_CondSignal(q->cond)来通知其它线程去读取消息。

(5)由这个函数ijkmp_get_msg(IjkMediaPlayer *mp, AVMessage *msg, int block)去读取消息队列的消息(如暂停的消息),这个函数在前面也已经分析过了,即可以送到java层,也可以送到ffplay层,源码如下:

9.播放流程测试

在bin目录下,这个目录有这个日志文件:

10.IJK播放器时序

下面继续讲讲,如何从java层一直到ffplay的函数调用和分析。java层到ffplay的时序图如下:

(1)

(2)

ijkMediaPlayer.java

(3)进入底层

(4)对接native层的文件,这是一种静态注册的方法。

(5)对接的就是ijkplayer_jni.c(这一层全是对接的java的native方法)的函数static void IjkMediaPlayer_setDataSourceCallback(JNIEnv *env, jobject thiz, jobject callback)

(6)再到了ijkplayer.c,调用这个函数int ijkmp_set_data_source(IjkMediaPlayer *mp, const char *url)。

(7)在ijkplayer.c文件中,代码风格是这样的,如ijkmp_set_data_source,主要是负责加锁,避免多线程的问题。那真正干活的就是ijkmp_set_data_source_I。保存地址,更改播放器状态。如下:

11.播放流程

(1)在文件IjkMediaPlayer.java中,函数prepareAsync()中,调用如下函数:

异步准备调用:

(2)在前面已经讲了设置好url的流程,就准备开始播了。如下调用关系:

(3)会调到文件Ijkplayer_jni.c的函数IjkMediaPlayer_prepareAsync,函数如下:

(4)在文件ijkplayer.c,函数IjkMediaPlayer_prepareAsync会调用ijkmp_prepare_async(IjkMediaPlayer *mp),函数如下:

(5)往java层和ffplay.c都是同一个队列。使用ijkmp_get_msg(xxx)往ff_ffplay.c里去处理。使用post_event是往java层去处理。是往一边发,还是两边发,使用标志continue_wait_next_msg。如果jni用不到,那这个消息就直接在消息队列中,被释放掉了。

(6)在文件ff_ffplay.c(这里面就是ffplay的那一套了),函数ijkmp_prepare_async_l会调用ffp_prepare_async_l(FFPlayer *ffp, const char *file_name),在该函数里面,最重要的就是调用stream_open(ffp, file_name, NULL),函数如下:

(7)ffp->is = is;这里保存了VideoState *is = stream_open(ffp, file_name, NULL)的参数,结构体也是一个接着一个管理,每个模块对接的都是只有一个总管。

(8)在函数stream_open(FFPlayer *ffp, const char *filename, AVInputFormat *iformat),就是各种初始化,如frame的队列初始化,packet队列初始化,时钟,音量,线程等初始化。这里还添加了支持,硬解的操作。

(9)在文件ff_ffplay.c,创建的读线程read_thread(void *arg),这里就可以去打开文件了。与前面分析的ffplay源码的文章如出一辙。传递的参数是FFPlayer *ffp(这个是后面自定义封装的)。

(10)在read_thread线程里,并把消息及时放到消息队列ffp_notify_msg1(ffp, FFP_MSG_OPEN_INPUT),发送给java层;在read_thread线程,正真打开码流的函数是stream_component_open(ffp, st_index[AVMEDIA_TYPE_SUBTITLE]),如下图:

(11)在该函数下初始化音视频,字幕的解码线程,如下:

注意:个人认为,虽然ffplay功能齐全,也比较稳定,但是这个框架,设计的不是很合理。

这段代码是调用硬件,ffp->node_vdec = ffpipeline_open_video_decoder(ffp->pipeline, ffp);实际上硬解就是回调mediacode。在read_thread里,实际ijk后面还添加了码率统计。如有这样一行代码,如下:

ffp->stat.bit_rate = ic->bit_rate;

12.创建ffplayer对象

(1)创建ffplayer对象,是在文件ijkplayer.c的函数ijkmp_create(int (*msg_loop)(void*))。如下:

(2)真正创建是在ff_ffplay.c中,函数ffp_create(),如下调用:

(3)使用ffp_toggle_buffering(ffp, 1)先缓存,缓存够了,才播放。

(4)

(5)

(6)在read_thread线程函数,隔一段时间,会一直检测缓存是否有准备好。

13.总结

本篇文章通过大量的篇幅,理清了ijkPlayer从java层到ffplay的一个播放过程,对于基于ijkplayer的项目应用,具有十分重要的意义。除了理清整个播放过程,还把重要源码移植到qt平台,让qt能够吊起来,这也是具有十分好的实战学习。由于网上关于ijkplayer非常详细的文章,非常少,所以这篇文章也是花了很多心血总结,所以也是非常值得推荐给大家。欢迎关注,收藏,转发,分享。

后期关于项目知识,也会更新在微信公众号“记录世界 from antonio”,欢迎关注

转载记得注明出处,不要随意复制,黏贴,创作不易,支持原创

本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com

点赞 0
收藏 0

文章为作者独立观点不代本网立场,未经允许不得转载。