ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

iOS 音频格式转换怎么实现?从 AVAudioFile、AAC、MP3 到 FLAC 与批量转码架构

iOS 音频格式转换怎么实现?从 AVAudioFile、AAC、MP3 到 FLAC 与批量转码架构 iOS 音频格式转换怎么实现从 AVAudioFile、AAC、MP3 到 FLAC 与批量转码架构在前面的文章中我们已经逐渐把文件浏览器扩展成一个完整的文件处理平台01 iOS 文件浏览器架构 02 GB 级大文件处理 03 Wi-Fi 文件传输 04 SMB / NAS 05 WebDAV 06 FTP 07 ZIP / RAR / 7z 08 图片转换与压缩 09 视频转换与压缩这一篇继续补齐媒体处理的另一块核心能力音频格式转换用户真正遇到的问题往往非常直接FLAC 太大怎么转 MP3或者WAV 文件几百 MB能不能压小一点又或者这个音频格式我的播放器打不开怎么转换从产品角度看就是Input Audio ↓ Convert ↓ Output Audio但从技术角度看音频转换背后至少涉及Container / File FormatCodecSample RateBit DepthChannel CountBitratePCMLossy / LosslessMetadataDurationEncoder / DecoderResamplingChannel Mixing所以音频格式转换并不等于修改扩展名。真正的音频转换通常是Decode ↓ PCM ↓ Optional Processing ↓ Encode这一篇就从文件管理 App 的角度系统拆解一套可扩展的 AudioService 架构。1. MP3、AAC、WAV、FLAC 到底有什么区别普通用户通常会把MP3 AAC WAV FLAC都叫音频格式。但技术上它们并不是完全同一个层级的概念。最简单可以先分成Compressed Audio Uncompressed Audio以及Lossy Lossless例如MP3 AAC通常属于有损压缩而FLAC通常属于无损压缩WAV 则经常作为PCM Audio Container存在。所以WAV本身并不等于一定完全无压缩的某一种固定编码。2. 最重要的概念PCM理解音频转换之前先理解PCM可以简单认为PCM 是比较接近“解码后的数字音频采样数据”的形式。例如MP3 File ↓ MP3 Decoder ↓ PCM Samples然后PCM Samples ↓ AAC Encoder ↓ AAC File所以MP3 → AAC本质上更像MP3 ↓ Decode PCM ↓ Encode AAC这和上一篇视频里的Decode → Raw Frames → Encode非常类似。3. 音频格式转换为什么不能直接改扩展名例如song.flac直接改成song.mp3内部数据还是FLAC所以播放器真正读取时仍然可能失败。正确流程song.flac ↓ FLAC Decoder ↓ PCM ↓ MP3 Encoder ↓ song.mp3所以Extension 只描述文件名Codec 才决定真正的数据结构。4. 什么是 Lossy例如MP3 AAC通常属于Lossy Compression也就是为了降低文件大小会丢弃一部分音频信息。例如WAV 50 MB转换为MP3 5 MB体积可能大幅下降。代价就是Audio Information Loss所以MP3 → WAV并不能把之前损失的信息恢复回来。5. MP3 转 WAV 不等于音质变好这是非常典型的误区。例如MP3 128 kbps转成WAV最终文件可能从4 MB变成40 MB但音质不会自动变成原始无损音质。因为MP3 Encoding阶段已经丢失的信息无法凭空恢复。所以MP3 → WAV只是将已经解码出来的 PCM 数据保存成另一种文件形式。文件更大不代表质量更高。6. FLAC 为什么叫无损压缩FLAC 的特点是PCM ↓ Lossless Compression ↓ FLAC然后FLAC ↓ Decode ↓ Original PCM理论目标是可以恢复原始音频采样数据。所以FLAC → WAV通常属于Lossless Decode只要整个转换链正确不会因为 FLAC 解码本身产生 MP3 那种有损压缩损失。7. FLAC 转 MP3 会发生什么流程FLAC ↓ Decode ↓ PCM ↓ MP3 Encode ↓ MP3这里损失发生在MP3 Encode阶段。所以FLAC → MP3是Lossless Source → Lossy Destination适合Reduce File Size Improve Compatibility但不适合追求保留完全原始音频数据。8. AAC 和 MP3 应该怎么理解两者都是非常常见的有损音频编码方案。从用户视角可以简单理解MP3 → 兼容性非常广 AAC → Apple / MP4 / Streaming 场景非常常见在某些相近码率场景下AAC 可以有很不错的压缩效率。但产品设计不能只看谁更先进还需要看Compatibility因为用户可能就是需要MP3上传到某个老系统。9. 一个音频文件到底由哪些参数决定音频文件大小和质量通常受到Codec Sample Rate Bit Depth Channels Bitrate Duration影响。例如44.1 kHz 16-bit Stereo和96 kHz 24-bit Stereo数据规模明显不同。所以一个 AudioService 不能只知道文件后缀而应该先Inspect Audio10. AudioInfo 应该包含什么例如structAudioInfo{letduration:TimeIntervalletsampleRate:DoubleletchannelCount:IntletbitRate:Int64?letbitDepth:Int?letcodec:AudioCodec?letformat:AudioFileFormatletfileSize:Int64}UI 可以展示FLAC 44.1 kHz 16-bit Stereo 3:42 28.4 MB这样用户才知道自己到底在处理什么。11. Sample Rate 是什么例如44.1 kHz大致意味着每秒对音频波形进行 44,100 次采样。常见值44.1 kHz 48 kHz 96 kHz并不是Sample Rate 越高任何音频都会自动更好。例如源文件本来44.1 kHz强行转96 kHz并不会凭空产生新的真实高频信息。这和720p 图片放大成 4K类似。12. Resampling 是什么如果Source: 96 kHz目标48 kHz就需要Resampling也就是PCM 96k ↓ Sample Rate Converter ↓ PCM 48k所以 AudioService 内部可能不仅是Decode Encode还可能是Decode ↓ Resample ↓ Encode13. Channel Count 也可能发生转换例如源文件5.1 Channels目标格式或用户需求Stereo则需要Downmix流程5.1 PCM ↓ Channel Mixer ↓ Stereo PCM同样Stereo ↓ Mono也是一种 Channel Conversion。所以Audio Convert实际上可能包括Codec Conversion Sample Rate Conversion Channel Conversion14. Bit Depth 是什么PCM 音频里常见16-bit 24-bit 32-bit FloatBit Depth 会影响Dynamic Range PCM Data Size例如24-bit PCM通常比16-bit PCM占用更多数据。但是MP3 AAC这类有损 Codec 更常用Bitrate描述压缩后的目标数据量。所以Bit Depth 和 Bitrate 不是一回事。15. Bitrate 对 MP3 / AAC 文件大小非常重要例如128 kbps 192 kbps 256 kbps 320 kbps粗略来说File Size ≈ Bitrate × Duration例如128 kbps × 240 sec大约30,720 kb再除以8大约3.84 MB实际还会有Container Metadata Encoder带来的差异。但用于估算已经非常有价值。16. 为什么 WAV 文件特别大假设44.1kHz 16-bit Stereo每秒原始 PCM 数据大约44,100 × 16 × 2bits。约1,411,200 bits/sec也就是≈ 1,411 kbps而 MP3 可能128 kbps所以两者体积自然差异巨大。这就是为什么10MB MP3转 WAV 以后可能变成100MB17. AudioService 应该和 FileProvider 分开继续沿用整个系列的架构FileProvider负责文件来自哪里。例如Local SMB WebDAV FTPAudioService 负责这个音频怎么处理。所以FileProvider ↓ FileItem ↓ AudioService而不是SMBAudioConverter FTPAudioConverter LocalAudioConverter18. 可以定义统一 AudioService例如protocolAudioService{funcinspect(_item:FileItem)asyncthrows-AudioInfofuncconvert(_item:FileItem,options:AudioConvertOptions)asyncthrows-FileItemfunccompress(_item:FileItem,options:AudioCompressOptions)asyncthrows-FileItem}其中AudioConvertOptions可以包含Output Format Codec Sample Rate Channels Bitrate Metadata Policy19. AVAudioFile 可以作为重要的音频文件抽象在 Apple 的音频体系中AVAudioFile可以作为很多音频文件操作的入口。整体思路File URL ↓ AVAudioFile ↓ Audio Format ↓ PCM Buffer然后PCM Buffer ↓ Output Audio File对于PCM格式转换Audio Engine 流程都很有价值。但是不应该假设所有你想支持的 Codec 和文件封装都能靠单一 Apple API 自动完整覆盖。如果产品需要MP3 FLAC OGG Opus等广泛格式通常仍需要根据目标格式、系统能力以及所选底层库设计不同实现策略。20. 一个典型音频转换 Pipeline例如FLAC → AAC可以抽象成FLAC File ↓ Decoder ↓ PCM Buffer ↓ Audio Converter ↓ AAC Encoder ↓ Output File如果需要修改采样率FLAC ↓ PCM 96kHz ↓ Resampler ↓ PCM 48kHz ↓ AAC Encoder如果还需要转换声道5.1 ↓ Mixer ↓ Stereo所以完整 PipelineDecoder ↓ Sample Rate Converter ↓ Channel Mixer ↓ Encoder21. 不要把整个音频一次读进内存比如8 hour WAV可能非常大。错误方向Audio File ↓ Data ↓ Memory或者Entire PCM ↓ RAM更合理的是Input File ↓ PCM Buffer ↓ Convert ↓ Output ↓ Next Buffer也就是Chunk / Buffer Based Processing这样Audio Duration 8 hours并不意味着Memory Usage 整个 8 小时 PCM22. 为什么音频处理中 PCM 特别容易占内存例如96 kHz 24-bit Stereo虽然源 FLAC 文件可能只有几百 MB解码后的 PCM 数据量会明显增加。所以Compressed Audio Size ≠ Decoded PCM Memory这和前面HEIC → Raw Pixels以及ZIP → Expanded Data是一样的资源管理问题。23. Audio Buffer 应该循环复用例如Read Buffer ↓ Convert ↓ Write ↓ Reuse Buffer而不是Buffer 1 Buffer 2 Buffer 3 ... 全部留在内存长音频转换尤其需要Bounded Memory也就是文件再长内存峰值依然应该受到控制。24. 音频进度怎么计算相比视频音频也很适合根据Processed Frames计算。例如Processed Frames ÷ Total Frames或者Processed Time ÷ Duration最终转换成0.0 ... 1.0UIConverting... 02:10 / 05:00 43%这样上层根本不需要理解Sample Buffer细节。25. 音频格式转换和音频压缩要分开比如ConvertFLAC → WAV重点Compatibility / FormatCompressWAV 500MB ↓ AAC 60MB重点File Size因此产品上最好提供Convert Audio和Compress Audio两个用户意图明确的入口。26. “压缩音频”最直接的方法是降低 Bitrate例如AAC 256 kbps降低到128 kbps文件大小粗略可以下降很多。如果再Stereo → Mono在语音场景下还可能进一步降低。但是音乐Stereo通常有明显价值。因此不能所有音频都默认Mono27. 语音和音乐应该采用不同策略这是产品设计很重要的一点。Voice例如Meeting Lecture Voice Memo Podcast Speech可以更倾向Lower Bitrate Mono Reduced Sample RateMusic则可能更适合Stereo Higher Bitrate 44.1 / 48 kHz所以可以提供Voice Music High Quality这样的预设。而不是让用户自己理解48kHz / 128kbps / Stereo是什么意思。28. 可以提供三种简单压缩模式例如Small优先文件大小Balanced大小与音质平衡High Quality优先保留音质高级设置再提供Codec Bitrate Sample Rate Channel这和前面的Image Video产品设计保持一致。29. “压到 10MB 以下”也可以做和图片、视频一样用户可能遇到Upload Limit: 10 MB这时可以根据Duration估算目标 Bitrate。例如Duration: 600 sec Target: 10 MB粗略总 Bitrate10 × 8 × 1024 ÷ 600约136 kbps再根据Container Overhead留出余量。然后选择例如128 kbps输出。30. 音频目标大小比视频更容易估算因为对于固定 Bitrate 编码File Size和Bitrate × Duration关系相对直接。例如128 kbps10 分钟大约就是一个比较容易估算的范围。所以Target File Size在音频工具里非常适合产品化。31. VBR 和 CBR 又有什么区别CBRConstant Bitrate码率相对固定。优点大小容易估计VBRVariable Bitrate复杂片段可以使用更多数据简单片段使用更少数据。通常更有利于Quality / Size Balance但最终文件大小预测不会像固定码率一样简单。所以如果用户强制要求目标大小CBR / ABR 思路通常更容易预测如果用户更看重质量效率则可考虑 VBR。32. 批量音频转换是高频需求例如用户有80 FLAC Files需要FLAC → MP3这类场景非常适合Batch Convert流程Select Folder ↓ Choose MP3 ↓ Choose Quality ↓ Start ↓ 80 Tasks这比一首一首点有价值得多。33. 但批量音频也要控制并发假设100 Files全部同时Decode Encode会导致CPU Pressure Memory Pressure Disk IO Pressure所以仍然需要Operation Queue限制Max Concurrent Audio Tasks具体数量应该通过Device Benchmark决定。而不是CPU 8 核所以同时跑 8 个。34. 音频转换比视频更适合有限并发和视频相比音频编码资源压力通常小很多。因此2–4 audio tasks在某些设备和场景下可能比较合理。但这不是固定数字。如果源文件是192kHz / 32-bit / Multichannel处理成本也会明显增加。核心还是并发度应该由实际资源占用决定。35. 单首失败不应该让整个批次失败例如100 FLAC其中99 Success 1 Corrupted应该Completed with 1 error而不是Batch Failed用户可以查看Failed Items然后Retry所以继续复用BatchOperation的Partial Success机制。36. Metadata 是否保留音频文件可能包含Title Artist Album Genre Year Track Number Artwork例如FLAC ↓ MP3用户通常希望Artist Album Cover继续存在。所以 AudioService 不能只处理PCM还要考虑Metadata Mapping37. 不同格式 Metadata 并不完全一样例如MP3常见ID3FLAC 有自己的 Metadata Block 体系。MP4 / M4A 也有自己的 Metadata 结构。所以Source Metadata ↓ Unified AudioMetadata ↓ Destination Metadata Writer会比原始 Tag 直接复制更加可扩展。38. 可以定义统一 AudioMetadata例如structAudioMetadata{lettitle:String?letartist:String?letalbum:String?letgenre:String?letyear:Int?lettrackNumber:Int?letartwork:Data?}然后FLAC Metadata ↓ AudioMetadata ↓ MP3 Metadata Writer这样格式转换的时候Audio Content和Metadata都可以迁移。39. Album Artwork 可能很大有些歌曲Audio 4MB但封面可能5MB PNG最后文件9MB所以做Audio Compression时如果用户目标是极小文件还应该考虑Artwork Resize / Compression例如3000 × 3000 PNG ↓ 1000 × 1000 JPEG否则音频码率已经很低文件还是不小。40. 但不要偷偷删除用户封面和图片 Metadata 一样。产品可以提供Keep Artwork默认开启。高级压缩时提供Compress Artwork Remove Artwork让用户自己决定。否则用户可能发现转换以后专辑封面没了。41. 音频转换任务仍然应该使用临时文件例如song.mp3转换过程中.song.mp3.tmp流程Create Temp ↓ Encode ↓ Finish ↓ Validate ↓ Rename失败Delete Temp这样可以避免半个 MP3出现在正常文件列表里。42. 如果覆盖原文件怎么办例如用户希望Replace Original仍然应该Source ↓ Create Temp Output ↓ Complete ↓ Validate ↓ Replace而不是Delete Source ↓ Convert否则Encoder Failed时用户原文件也可能没了。43. 同名文件继续使用 ConflictResolver例如song.flac转song.mp3目录已经存在song.mp3继续统一处理Replace Keep Both Skip CancelKeep Bothsong (1).mp3这样Copy Extract Image Convert Video Convert Audio Convert全部共享ConflictResolver44. 远程音频怎么办例如NAS ↓ song.flac用户Convert to MP3最简单方案SMB ↓ Download Temp ↓ AudioService ↓ MP3然后Save Local如果用户需要保存回 NASMP3 ↓ Upload ↓ SMB这和视频处理逻辑一样。45. 音频比视频更适合流式远程转换吗理论上某些顺序音频处理很适合Remote Read Stream ↓ Decoder ↓ PCM ↓ Encoder ↓ Destination Stream也就是NAS FLAC ↓ iPhone Transcode ↓ WebDAV MP3无需完整落地源文件。但这需要底层Decoder Provider Encoder都能够很好地支持流式数据源和流式输出。所以它属于更高级的架构。第一版完全可以Remote → Local Temp → Process先保证可靠性。46. AudioDataSource 也可以抽象和ArchiveDataSource ImageDataSource VideoDataSource一样。可以进一步AudioDataSource例如protocolAudioDataSource{funcread(offset:Int64,length:Int)asyncthrows-Data}上层AudioService不一定知道数据来自Local SMB WebDAV但同样要尊重底层音频框架的真实能力。不要为了统一 API 而强行做不合适的抽象。47. 音频预览和音频转换应该分开用户只是点击song.mp3播放。应该AudioPlayerService处理。而FLAC → MP3是AudioService处理。所以Playback ≠ Conversion不要把所有音频功能塞进一个巨大AudioManager48. Waveform 又应该属于哪里如果产品显示Audio Waveform通常可以单独设计WaveformService流程Audio ↓ Downsample Samples ↓ Amplitude Data ↓ Waveform Cache而不是每次打开页面都Decode Entire Audio重新生成。和Image Thumbnail Video Thumbnail思路类似。49. Waveform 也应该缓存例如缓存 KeyFile ID Modified Date Waveform Resolution文件没变Return Cache文件更新Regenerate对于 WebDAVETag也可以帮助判断缓存是否过期。50. Audio Operation 进入 FileOperationManager目前任务系统已经拥有Copy Upload Download Extract Compress Image Convert Video Convert继续增加Audio Convert Audio Compress例如Converting Audio 23 / 80 29%任务状态Waiting Preparing Running Cancelled Completed Failed这样所有长任务体验保持一致。51. 音频错误也要业务化不要只显示Conversion Failed可以定义enumAudioProcessingError:Error{caseunsupportedFormatcaseunsupportedCodeccaseinvalidAudiocaseencoderUnavailablecaseinsufficientStoragecasedestinationConflictcasecancelledcaseunknown(Error)}例如Unsupported CodecUI 可以进一步提示当前版本暂不支持将该音频编码格式转换为 MP3。比OSStatus -xxxx有意义很多。52. OGG 和 Opus 需要特别看底层能力如果产品计划支持OGG Opus不能只在enum AudioFormat里增加两个 case 就结束。需要确认Decoder Encoder Container Metadata Seeking Duration实际是否由系统框架或者第三方库支持。尤其商业 App 还需要检查License这和上一篇 RAR / 7z 的原则一样支持一种格式是一个完整能力不是增加一个扩展名。53. 第三方音频库最好封装在 Adapter 后面如果MP3 AAC用方案 A。FLAC用方案 B。Opus用方案 C。不要让业务层知道。可以AudioCodecAdapter │ ├── SystemAudioAdapter ├── FLACAdapter └── OpusAdapter上层AudioService统一调用Decode Encode这样以后更换底层实现不会影响 UI 和 FileOperation。54. 可以设计 AudioStrategyResolver类似上一篇视频。例如Audio Task ↓ Inspect ↓ Strategy Resolver决定Direct Copy? Remux? Decode Encode? Resample? Downmix?例如AAC in M4A ↓ AAC in MP4-compatible output某些场景可能不用完整重新编码。而FLAC → MP3显然需要Decode Encode所以能避免重新编码时也应优先避免无意义的二次有损转换。55. 有损音频不要反复转换例如MP3 ↓ AAC ↓ MP3 ↓ AAC每次有损重新编码都可能进一步降低质量。所以如果源已经是MP3用户只是想减小文件应优先直接重新编码一次到目标码率。不要在内部经过多个有损中间文件。最合理 PipelineSource ↓ PCM ↓ Final Destination Codec而不是MP3 ↓ AAC Temp ↓ MP3 Final56. PCM 中间状态最好只存在于内存 Buffer如果只是为了FLAC → MP3通常没有必要FLAC ↓ 50GB WAV Temp ↓ MP3更好的方式FLAC Decoder ↓ PCM Buffer ↓ MP3 Encoder也就是Decode and Encode in Pipeline这样节省磁盘减少 IO更快更容易流式处理57. 音频文件处理仍然要考虑磁盘空间虽然 PCM 不一定完整落地目标文件还是需要空间。例如批量200 FLAC总计30GB转换 MP3 输出预计8GB设备只剩3GB最好提前提示。所以继续复用StorageChecker。58. 一个完整 AudioService 架构最终可以整理┌─────────────────────────────┐ │ UI │ │ │ │ Convert Audio │ │ Compress Audio │ │ Batch Convert │ └──────────────┬──────────────┘ │ ▼ ┌─────────────────────────────┐ │ FileOperationManager │ │ │ │ Queue │ │ Progress │ │ Cancel │ │ Retry │ └──────────────┬──────────────┘ │ ▼ ┌─────────────────────────────┐ │ AudioService │ │ │ │ Inspect │ │ Strategy Resolver │ │ Decode │ │ Resample │ │ Mix │ │ Encode │ └──────────────┬──────────────┘ │ ┌───────┼────────┐ ▼ ▼ ▼ System FLAC Other Codec Audio Adapter Adapter │ ▼ PCM Buffer旁边继续存在MetadataService WaveformService StorageChecker TemporaryFileManager ConflictResolver Credential / Provider Layer59. 整个媒体架构现在已经完整很多现在Services │ ├── ArchiveService ├── ImageService ├── VideoService ├── AudioService ├── ThumbnailService └── WaveformService下面FileProvider │ ├── Local ├── SMB ├── WebDAV └── FTP中间统一FileOperationManager于是一个文件可以找到 ↓ 打开 ↓ 复制 ↓ 转换 ↓ 压缩 ↓ 分享整个体验不用跳出文件管理器。60. TS File Explorer 的音频功能真正解决什么从开发者视角PCM Sample Rate Bitrate Encoder AVAudioFile是技术。但用户真正的问题是FLAC 太大手机里占空间。或者WAV 发不出去。或者我的播放器只支持 MP3。所以真正产品流程可能是FLAC ↓ TS File Explorer ↓ Convert to MP3 ↓ Share或者500MB WAV ↓ Compress ↓ 70MB AAC ↓ Upload用户真正需要的不是一个 Codec 面板。而是这个音频文件现在能不能帮我处理成我要的样子。61. 开发音频转换模块前建议先回答这 20 个问题1. 支持哪些输入格式 2. 支持哪些输出格式 3. 哪些 Codec 使用系统能力 4. 哪些需要第三方实现 5. PCM Pipeline 如何设计 6. 是否支持 Sample Rate Conversion 7. 是否支持 Channel Conversion 8. Bit Depth 怎么处理 9. MP3 / AAC Bitrate 如何选择 10. 是否支持目标文件大小 11. 是否支持 CBR / VBR 12. Metadata 是否保留 13. Artwork 是否保留 14. 大文件是否 Buffer Streaming 15. 批量任务并发度多少 16. 单文件失败是否影响批次 17. 临时文件如何处理 18. Remote Audio 如何处理 19. 第三方 Codec License 是否可商用 20. Audio Operation 如何接入 FileOperationManager提前把这些问题想清楚以后音频模块才不会从一个 MP3 转换按钮最后演变成几十个格式判断 不同 Encoder 特判 Metadata Bug 批量任务 Bug写在最后音频转换表面看起来只是FLAC → MP3但真正的架构实际上是Decode PCM Sample Rate Channels Bitrate Metadata Encode File Operation和图片、视频一样不要把音频模块设计成一堆“格式 A 转格式 B”的函数。否则最后很容易出现flacToMp3() wavToMp3() aacToMp3() mp3ToWav() flacToWav()组合越来越多。更合理的设计应该是Source Audio ↓ Inspect ↓ Decode ↓ PCM Pipeline ↓ Encode ↓ Destination Format这样未来增加新格式只需要扩展Decoder / Encoder Adapter而不需要重新组合所有转换路径。到这里整个系列已经完成01 File Browser ↓ 02 Large File IO ↓ 03 Wi-Fi Transfer ↓ 04 SMB ↓ 05 WebDAV ↓ 06 FTP ↓ 07 Archive ↓ 08 Image ↓ 09 Video ↓ 10 Audio下一篇我建议进入 TS File Explorer 另一个非常实用、同时搜索需求也很明确的模块《iOS PDF 工具怎么实现从 PDFKit、标注、签名到扫描生成 PDF》可以继续拆解PDFKit PDFView PDFDocument Annotation Highlight Underline Pen Signature Page Rendering Camera Scan Image → PDF Large PDF Thumbnail Cache这样就从媒体文件处理继续扩展到Document Processing。
返回列表