Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Android端的短视频开发,我们该如何快速实现移动端短视频功能?

Android端的短视频开发,我们该如何快速实现移动端短视频功能?

作者头像
Android技术干货分享
发布于 2019-03-27 03:33:49
发布于 2019-03-27 03:33:49
2.1K00
代码可运行
举报
文章被收录于专栏:Android技术分享Android技术分享
运行总次数:0
代码可运行

当下抖音非常火热,是不是也很心动做一个类似的app吗?

一.短视频内容生产

优质短视频内容的产生依赖于短视频的采集和特效编辑,这就要求在进行抖音APP开发时,用到基础的美颜、混音、滤镜、变速、图片视频混剪、字幕等功能,在这些功能基础上,进行预处理,结合OpenGL、AI、AR技术,产生很多有趣的动态贴纸玩法,使得短视频内容更具创意。

视频录制的大致实现流程是先由 Camera 、 AudioRecord 进行最原始的相机画面以及声音的采集,然后将采集的数据进行滤镜、降噪等前处理,处理完成后由 MediaCodec 进行硬件编码,最后采用 MediaMuxer 生成最终的 MP4 文件。

二.短视频处理播放

视频的处理和播放主要是视频的清晰度、观看流畅度方面的体验。在这方面来讲,可以采用“窄带高清”技术,在节省码率的同时能够提供更加清晰的观看体验,经过测试,同等视频质量下最高可以节省20-40%带宽。除了带宽之外,短视频内容的存储和CDN优化也尤为重要,通常我们需要上传到云存储服务器的内容是短视频内容和封面内容。

而CDN优化带给短视频平台的则是进一步的短视频首次载入和循环播放方面的体验。比如针对首播慢的问题,像阿里云播放器支持QUIC协议,基于CDN的调度,可以使短视频首次播放秒开的成功率达到98%,此外在循环播放时还可以边播放边缓存,用户反复观看某一短视频时就不用耗费流量了。

三.录制视频的方式

Android系统当中,如果需要一台Android设备来获取到一个MP4这样的视频文件的话,主流的方式一共与三种:MediaRecorder、MediaCodec+MediaMuxer、FFmpeg。

MediaRecorder:是Android系统直接提供给我们的录制类,用于录制音频和视频的一个类,简单方便,不需要理会中间录制过程,结束录制后可以直接得到音频文件进行播放,录制的音频文件是经过压缩的,需要设置编码器,录制的音频文件可以用系统自带的播放器播放。

优点:大部分以及集成,直接调用相关接口即可,代码量小,简单稳定;

缺点:无法实时处理音频;输出的音频格式不是很多。

MediaCodec+MediaMuxer: MediaCodec 与 MediaMuxer结合使用同样能够实现录制的功能。MediaCodec是Android提供的编解码类,MediaMuxer则是复用类(生成视频文件)。从易用性的角度上来说肯定不如MediaRecorder,但是允许我们进行更加灵活的操作,比如需要给录制的视频添加水印等各种效果。

优点: 与MediaRecorder一样低功耗速度快,并且更加灵活

缺点: 支持的格式有限,兼容性问题

FFmpeg: FFmpeg(Fast forword mpeg,音视频转换器)是一个开源免费跨平台的视频和音频流方案,它提供了录制/音视频编解码、转换以及流化音视频的完整解决方案。主要的作用在于对多媒体数据进行解协议、解封装、解码以及转码等操作

优点:格式支持非常的强,十分的灵活,功能强大,兼容性好;

缺点:C语言些的音视频编解码程序,使用起来不是很方便。

虽然从数据看来FFmpeg是最好的,但是我们得首先排除这种,因为他的易用性是最差的;其次,MediaRecorder也是需要排除的,所以在这里我比较推荐MediaCodec+MediaMuxer这种方式。

四.编码器参数

码率:数据传输时单位时间传送的数据位数,kbps:千位每秒。码率和质量成正比,也和文件体积成正比。码率超过一定数值,对图像的质量没有多大的影响。

帧数:每秒显示多少个画面,fps

关键帧间隔:在H.264编码中,编码后输出的压缩图像数据有多种,可以简单的分为关键帧和非关键帧。关键帧能够进行独立解码,看成是一个图像经过压缩的产物。而非关键帧包含了与其他帧的“差异”信息,也可以称呼为“参考帧”,它的解码需要参考关键帧才能够解码出一个图像。非关键帧拥有更高的压缩率。

五、MediaCodec+MediaMuxer的使用

MediaMuxer和MediaCodec这两个类,它们的参考文http://developer.android.com/reference/android/media/MediaMuxer.htmlhttp://developer.android.com/reference/android/media/MediaCodec.html,里边有使用的框架。这个组合可以实现很多功能,比如音视频文件的编辑(结合MediaExtractor),用OpenGL绘制Surface并生成mp4文件,屏幕录像以及类似Camera app里的录像功能(虽然这个用MediaRecorder更合适)等。

它们一个是生成视频,一个生成音频,这里把它们结合一下,同时生成音频和视频。基本框架和流程如下:

首先是录音线程,主要参考HWEncoderExperiments。通过AudioRecord类接收来自麦克风的采样数据,然后丢给Encoder准备编码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
AudioRecord audio_recorder; 
audio_recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, 
 SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT, buffer_size); 
// ... 
audio_recorder.startRecording(); 
while (is_recording) { 
 byte[] this_buffer = new byte[frame_buffer_size]; 
 read_result = audio_recorder.read(this_buffer, 0, frame_buffer_size); // read audio raw data 
 // … 
 presentationTimeStamp = System.nanoTime() / 1000; 
 audioEncoder.offerAudioEncoder(this_buffer.clone(), presentationTimeStamp); // feed to audio encoder 

} 

这里也可以设置AudioRecord的回调(通过setRecordPositionUpdateListener())来触发音频数据的读取。offerAudioEncoder()里主要是把audio采样数据送入音频MediaCodec的InputBuffer进行编码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ByteBuffer[] inputBuffers = mAudioEncoder.getInputBuffers(); 
int inputBufferIndex = mAudioEncoder.dequeueInputBuffer(-1); 
if (inputBufferIndex >= 0) { 
 ByteBuffer inputBuffer = inputBuffers[inputBufferIndex]; 
 inputBuffer.clear(); 
 inputBuffer.put(this_buffer); 
 ... 
 mAudioEncoder.queueInputBuffer(inputBufferIndex, 0, this_buffer.length, presentationTimeStamp, 0); 
} 

下面,参考Grafika-SoftInputSurfaceActivity,并加入音频处理。主循环大体分四部分:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
try { 
 // Part 1 
 prepareEncoder(outputFile); 
 ... 
 // Part 2 
 for (int i = 0; i < NUM_FRAMES; i++) { 
 generateFrame(i); 
 drainVideoEncoder(false); 
 drainAudioEncoder(false); 
 } 
 // Part 3 
 ... 
 drainVideoEncoder(true); 
 drainAudioEncoder(true); 
} catch (IOException ioe) { 
 throw new RuntimeException(ioe); 
} finally { 
 // Part 4 
 releaseEncoder(); 
} 

第1部分是准备工作,除了video的MediaCodec,这里还初始化了audio的MediaCodec:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
MediaFormat audioFormat = new MediaFormat(); 
audioFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100); 
audioFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1); 
... 
mAudioEncoder = MediaCodec.createEncoderByType(AUDIO_MIME_TYPE); 
mAudioEncoder.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); 
mAudioEncoder.start(); 

第2部分进入主循环,app在Surface上直接绘图,由于这个Surface是从MediaCodec中用createInputSurface()申请来的,所以画完后不用显式用queueInputBuffer()交给Encoder。drainVideoEncoder()和drainAudioEncoder()分别将编码好的音视频从buffer中拿出来(通过dequeueOutputBuffer()),然后交由MediaMuxer进行混合(通过writeSampleData())。注意音视频通过PTS(Presentation time stamp,决定了某一帧的音视频数据何时显示或播放)来同步,音频的time stamp需在AudioRecord从MIC采集到数据时获取并放到相应的bufferInfo中,视频由于是在Surface上画,因此直接用dequeueOutputBuffer()出来的bufferInfo中的就行,最后将编码好的数据送去MediaMuxer进行多路混合。

注意这里Muxer要等把audio track和video track都加入了再开始。MediaCodec在一开始调用dequeueOutputBuffer()时会返回一次INFO_OUTPUT_FORMAT_CHANGED消息。我们只需在这里获取该MediaCodec的format,并注册到MediaMuxer里。接着判断当前audio track和video track是否都已就绪,如果是的话就启动Muxer。

总结来说,drainVideoEncoder()的主逻辑大致如下,drainAudioEncoder也是类似的,只是把video的MediaCodec换成audio的MediaCodec即可。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
while(true) { 
 int encoderStatus = mVideoEncoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC); 
 if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) { 
 ... 
 } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 
 encoderOutputBuffers = mVideoEncoder.getOutputBuffers(); 
 } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 
 MediaFormat newFormat = mAudioEncoder.getOutputFormat(); 
 mAudioTrackIndex = mMuxer.addTrack(newFormat); 
 mNumTracksAdded++; 
 if (mNumTracksAdded == TOTAL_NUM_TRACKS) { 
 mMuxer.start(); 
 } 
 } else if (encoderStatus < 0) { 
 ... 
 } else { 
 ByteBuffer encodedData = encoderOutputBuffers[encoderStatus]; 
 ... 
 if (mBufferInfo.size != 0) { 
 mMuxer.writeSampleData(mVideoTrackIndex, encodedData, mBufferInfo); 
 } 
 mVideoEncoder.releaseOutputBuffer(encoderStatus, false); 
 if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 
 break; 
 } 
 } 

} 

第3部分是结束录制,发送EOS信息,这样在drainVideoEncoder()和drainAudioEncoder中就可以根据EOS退出内循环。第4部分为清理工作。把audio和video的MediaCodec,MediaCodec用的Surface及MediaMuxer对象释放。

最后几点注意: 1. 在AndroidManifest.xml里加上录音权限,否则创建AudioRecord对象时铁定失败: <uses-permission android:name="android.permission.RECORD_AUDIO"/> 2. 音视频通过PTS同步,两个的单位要一致。 3. MediaMuxer的使用要按照Constructor -> addTrack -> start -> writeSampleData -> stop 的顺序。如果既有音频又有视频,在stop前两个都要writeSampleData()过。

总结

以上就是抖音类APP的部分内容,其中的步骤和过程是我亲自实践过的,按照上述的过程应该都可以正常运行,写这一篇文章花了很多时间,希望所有看了这篇文章的朋友们都能够有一定的收获。此外更多的Android短视频详细内容可见下方附带资料:

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019.01.23 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
短视频源码开发,Android端短视频功能的快速实现
一、短视频内容生产 优质短视频内容的产生依赖于短视频的采集和特效编辑,这就要求在进行短视频源码开发时,用到基础的美颜、混音、滤镜、变速、图片视频混剪、字幕等功能,在这些功能基础上,进行预处理,结合OpenGL、AI、AR技术,产生很多有趣的动态贴纸玩法,使得短视频内容更具创意。
云豹科技程序员
2021/06/01
1.1K0
刷抖音上瘾后,决定探究如果做一款类似抖音短视频app
优质短视频内容的产生依赖于短视频的采集和特效编辑,这就要求在进行抖音APP开发时,用到基础的美颜、混音、滤镜、变速、图片视频混剪、字幕等功能,在这些功能基础上,进行预处理,结合OpenGL、AI、AR技术,产生很多有趣的动态贴纸玩法,使得短视频内容更具创意。
Android技术干货分享
2019/06/17
2.5K2
Android音视频硬编码与混合(三)
硬编码:使用非CPU进行编码,如显卡GPU、专用的DSP、FPGA、ASIC芯片等
PengJie
2021/01/03
2.5K0
MediaCodec录制音视频并将合成为一个文件
音频录制 相关参考 MediaCodec硬编码pcm2aac 主要分为以下几步骤:
曾大稳
2020/01/20
2.3K0
MediaCodec录制音视频并将合成为一个文件
【Android 音视频开发打怪升级:OpenGL渲染视频画面篇】六、Android音视频硬编码:生成一个MP4
在【音视频硬解码流程:封装基础解码框架】这篇文章中,介绍了如何使用Android原生提供的硬编解码工具MediaCodec,对视频进行解码。同时,MediaCodec也可以实现对音视频的硬编码。
开发的猫
2020/04/02
2.1K1
【Android 音视频开发打怪升级:OpenGL渲染视频画面篇】六、Android音视频硬编码:生成一个MP4
「Android音视频编码那点破事」第五章,使用MediaCodec编码AAC音频数据
  在上一章我们讲到了MediaCodec的工作流程,以及如何利用MediaCodec进行H264编码。这一章的内容同样是MediaCodec,只不过是编码音频为AAC,整个流程大同小异。   上一章我们利用MediaCodec编码视频时,使用了Surface,所以可以不直接操作输入缓冲区队列。但是编码音频的时候,由于无法使用Surface,所以需要直接操作输入缓冲区队列。   这里我们需要通过AudioRecord采集PCM数据,然后把采集到的数据送进编码器进行编码。所以首先我们要初始化一个AudioRecord对象。   要使用录音,需要申请录音权限。
阿利民
2022/05/16
4930
干货 | 移动端使用OpenGL转场特效的音视频合成应用
近年来短视频的火爆,让内容创作类的APP获得了巨大的流量。用户通过这类工具编辑自己的短视频,添加各式各样的炫酷特效,从而呈现出更加丰富多彩的视频内容。本文将会介绍如何使用移动端原生API,将图片添加转场特效并且最终合成为视频的基本流程。
携程技术
2023/11/11
5110
干货 | 移动端使用OpenGL转场特效的音视频合成应用
「Android音视频编码那点破事」第四章,使用MediaCodec实现H264编码
  说到Android的视频硬编码,很多新人首先会想到MediaRecorder,这可以说是Android早期版本视频硬编码的唯一选择。这个类的使用很简单,只需要给定一个Surface(输入)和一个File(输出),它就给你生成一个标准的mp4文件。   但越是简单的东西便意味着越难以控制,MediaRecorder的缺点很明显。相信很多人在接触到断点视频录制这个需求的时候,首先会想到使用MediaRecorder,很遗憾,这个东西并不能给你很多期待,就像一开始的我一样。   首先,MediaRecorder并没有断点录制的API,当然你可以使用一些“小技巧”,每次录制的时候,都把MediaRecorder stop掉,然后再次初始化,这样就会生成一系列的视频,最后把它们拼接起来。然而问题在于,每次初始化MediaRecorder都需要消耗很长时间,这意味着,当用户快速点击录制按钮的时候可能会出现问题。对于这个问题,你可以等到MediaRecorder初始化完成才让用户点击开始录制,但是这样往往会因为等待时间过长,导致用户体验极差。   这种情况下,一个可控的视频编码器是必须的。虽然在Android 4.4以前我们没得选择,但是在Android 4.4之后,我们有了MediaCodec,一个完全可控的视频编码器,虽然无法直接输出mp4(需要配合MediaMuxer来对音视频进行混合,最终输出mp4,或者其它封装格式)。如今的Android生态,大部分手机都已经是Android 5.0系统,完全可以使用MediaCodec来进行音视频编码的开发,而MediaRecorder则降级作为一个提高兼容性的备选方案。   废话不多说,我们直接步入正题。要想正确的使用MediaCodec,我们首先得先了解它的工作流程,关于这个,强烈大家去看一下Android文档。呃呃,相信在这个快速开发为王道的环境,没几个人会去看,所以还是在这里简单介绍一下。
阿利民
2022/05/16
8560
「Android音视频编码那点破事」第四章,使用MediaCodec实现H264编码
【Android 音视频开发打怪升级:音视频硬解码篇】二、音视频硬解码流程:封装基础解码框架
MediaCodec 是Android 4.1(api 16)版本引入的编解码接口,同时支持音视频的编码和解码。
开发的猫
2020/04/01
3.4K0
【Android 音视频开发打怪升级:音视频硬解码篇】二、音视频硬解码流程:封装基础解码框架
Android 音频开发入门指南
Android 平台提供了一套丰富的音频 API,使得开发者可以轻松地为应用添加音频播放、录制、处理等功能。这些 API 包括:
陆业聪
2024/07/23
2330
Android 音频开发入门指南
安卓支持aac编码吗_aac音频格式
Android音视频编码分为软编和硬编两种。所谓的硬编是用设备GPU去实现编解码,从而减轻CPU的压力,让程序更加的健壮,自然而然你就知道了软编其实就是让CPU编码(其实是在c层通过c/c++进行编码,之所以这样是因为c/c++平台上已经有很多比较好的音视频编解码库。比如著名ffmpeg,搞过音视频的相信对这个库绝对不会陌生)。那么或许你心目中有一个小小的疑问?为什么要编解码了?原因就是让数据更小便于传输。编解码就好比是压缩与解压!本文是把PCM数据硬编成ACC格式数据。如果对音频的采集不熟悉,请查阅Android 音频采集。
全栈程序员站长
2022/09/27
1.2K0
安卓支持aac编码吗_aac音频格式
视频直播技术干货(十二):从入门到放弃,快速学习Android端直播技术
从游戏、教育、电商到娱乐,直播技术的应用场景无处不在。随着移动端的网速越来越快,直播技术的普及和发展将更加迅速。
JackJiang
2024/10/17
1750
视频直播技术干货(十二):从入门到放弃,快速学习Android端直播技术
使用MediaCodeC将图片集编码为视频
这是MediaCodeC系列的第三章,主题是如何使用MediaCodeC将图片集编码为视频文件。在Android多媒体的处理上,MediaCodeC是一套非常有用的API。此次实验中,所使用的图片集正是MediaCodeC硬解码视频,并将视频帧存储为图片文件文章中,对视频解码出来的图片文件集,总共332张图片帧。 若是对MediaCodeC视频解码感兴趣的话,也可以浏览之前的文章:MediaCodeC解码视频指定帧,迅捷、精确
AiLo
2019/11/22
2.5K0
Android制作带悬浮窗控制的录屏程序Demo
最近开发的新版程序初版基本差不多了,所以抽空需要研究一下针对运维方便的辅助工具,其中就有需要做一个WIndows服务器可以远程控制Android客户端的工具,实现的原理大概已经有了个思路了,拆解后每个细节就需要去做技术验证,远程控制首先就需要做到看到对面的图像,预览图像就要使用录屏的功能,所以就有了这个小Demo,当然最终要做的东西是不需要保存本地视频的,这里是为了验证一下是否成功。
Vaccae
2021/10/12
1.8K0
【Android 音视频开发打怪升级:音视频硬解码篇】四、音视频解封和封装:生成一个MP4
在本篇章的第二篇文章【音视频硬解码流程】,已经讲过,Android使用的是MediaExtractor对音视频数据流进行解封。这里,我们简单再过一遍。
开发的猫
2020/04/01
5840
Android原生编解码接口 MediaCodec 之——踩坑
MediaCodec 有两种方式触发输出关键帧,一是由配置时设置的 KEY_FRAME_RATE 和KEY_I_FRAME_INTERVAL参数自动触发,二是运行过程中通过 setParameters 手动触发输出关键帧。
全栈程序员站长
2022/11/01
6.2K1
Android原生编解码接口 MediaCodec 之——踩坑
关于Android录屏程序在Android10下的修改
上一篇《Android制作带悬浮窗控制的录屏程序Demo》我自己用的虚拟机是Android8的版本,后来用自己的手机无法使用,原因是在Android 10之后录屏等功能要求在前台Service中进行,所以如果你的设备是Android 10以上的 ,上一篇中的录屏就不能用了,所以这篇是专门针对Android 10录屏做的改动。
Vaccae
2021/10/12
2.4K0
一文掌握直播技术:实时音视频采集、编码、传输与播放
从游戏、教育、电商到娱乐,直播技术的应用场景无处不在。随着移动端的网速越来越快,直播技术的普及和发展将更加迅速。
陆业聪
2024/08/19
9640
一文掌握直播技术:实时音视频采集、编码、传输与播放
【Android 音视频开发打怪升级:音视频硬解码篇】三、音视频播放:音视频同步
在上一篇文章定义的解码流程框架基类中,预留了几个虚函数,留给子类初始化自己的东西,本篇,就来看看如何实现。
开发的猫
2020/04/01
2.4K0
【Android 音视频开发打怪升级:音视频硬解码篇】三、音视频播放:音视频同步
MediaCodec进行AAC编解码(AudioRecord采集录音)
最近工作比较忙,很久没有更新这个系列的文章。我们先回顾一下上一篇MediaCodec进行AAC编解码(文件格式转换)的内容,里面介绍了MediaExtractor的使用,MediaCodec进行音频文件的解码和编码,ADTS的介绍和封装。今天这篇文章在此基础上跟大家一起学习如何通过Android设备进行音频的采集,然后使用MediaCodec进行AAC编码,最后输出到文件。这部分我们关注的重点就是在如何进行音频的采集。 项目代码github对应的代码版本v1.7。大家一定要注意下载对应的代码版本调试。
用户2929716
2018/08/23
1.8K0
MediaCodec进行AAC编解码(AudioRecord采集录音)
推荐阅读
短视频源码开发,Android端短视频功能的快速实现
1.1K0
刷抖音上瘾后,决定探究如果做一款类似抖音短视频app
2.5K2
Android音视频硬编码与混合(三)
2.5K0
MediaCodec录制音视频并将合成为一个文件
2.3K0
【Android 音视频开发打怪升级:OpenGL渲染视频画面篇】六、Android音视频硬编码:生成一个MP4
2.1K1
「Android音视频编码那点破事」第五章,使用MediaCodec编码AAC音频数据
4930
干货 | 移动端使用OpenGL转场特效的音视频合成应用
5110
「Android音视频编码那点破事」第四章,使用MediaCodec实现H264编码
8560
【Android 音视频开发打怪升级:音视频硬解码篇】二、音视频硬解码流程:封装基础解码框架
3.4K0
Android 音频开发入门指南
2330
安卓支持aac编码吗_aac音频格式
1.2K0
视频直播技术干货(十二):从入门到放弃,快速学习Android端直播技术
1750
使用MediaCodeC将图片集编码为视频
2.5K0
Android制作带悬浮窗控制的录屏程序Demo
1.8K0
【Android 音视频开发打怪升级:音视频硬解码篇】四、音视频解封和封装:生成一个MP4
5840
Android原生编解码接口 MediaCodec 之——踩坑
6.2K1
关于Android录屏程序在Android10下的修改
2.4K0
一文掌握直播技术:实时音视频采集、编码、传输与播放
9640
【Android 音视频开发打怪升级:音视频硬解码篇】三、音视频播放:音视频同步
2.4K0
MediaCodec进行AAC编解码(AudioRecord采集录音)
1.8K0
相关推荐
短视频源码开发,Android端短视频功能的快速实现
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验