移动开发中的转码——定义、工作原理及应用场景

作者: IT Sectr 发布日期: 2026-05-25 阅读时间: 10 分钟

Transcoding——将数字媒体文件从一种压缩格式完全解码并重新编码为另一种压缩格式的过程。与转封装(仅更改容器)不同,转码会更改编解码器、比特率、分辨率以及压缩流的其他参数。根据Apple AVFoundation文档(2026年)转码用于使内容适应不同的设备和网络条件。

要点

  • Transcoding——将原始流完全解码为未压缩的PCM,然后编码为新格式
  • Transmuxing仅更改容器而不对流进行重新编码——这不是转码
  • 转码的主要原因:更换编解码器、降低比特率、更改分辨率、适配DRM
  • 工具:FFmpeg、AVFoundation(iOS)、MediaCodec(Android)、AWS Elemental MediaConvert
  • 转码在移动设备上实时运行需要硬件编码器加速

什么是转码?

Transcoding——通过将原始流完全解码为中间未压缩的PCM格式,然后以新参数进行编码,将媒体文件从一种压缩格式转换为另一种压缩格式的过程。如果原始文件中视频使用H.264编解码器以10 Mbit/s的比特率压缩,而输出需要H.265以3 Mbit/s的比特率——这就是转码。

转码不同于简单的重新打包(转封装),后者仅更改容器(例如MP4转换为MKV),而压缩后的比特流本身保持不变。在转码过程中,会进行计算密集型的转换:解码每一帧、应用滤镜(缩放、色彩校正、裁剪)、以新参数重新编码。这使得转码成为处理媒体时最耗资源的操作之一。

转码广泛应用于各种任务:使视频适应网络带宽限制、转换为目标设备硬件解码支持的格式、为HLS/DASH流媒体创建多个版本、将音轨提取到单独文件。OTT服务(Netflix、YouTube、Twitch)将每个上传的文件转码为数十种具有不同比特率、分辨率和编解码器的版本,以便为数百万用户提供自适应流媒体服务。

转码如何工作?

转码过程包括三个主要阶段:解码、处理和编码。每个阶段都可以在CPU或GPU/硬件模块上执行,具体取决于可用性和所需性能。

转码的阶段

第一阶段——解码原始流。从容器(MP4、MOV、MKV)中读取原始文件,然后将压缩的视频数据包发送到解码器。解码可以是硬件解码(如果编解码器支持)或通过FFmpeg进行软件解码。解码输出为未压缩的YUV420或BGRA格式帧——正是在这个阶段,转码与简单的重新复用区分开来。

第二阶段——滤镜和处理。解码后的帧通过滤镜链:缩放到目标分辨率、改变帧率、色彩校正、叠加文本或图形。FFmpeg滤镜链被构建为图,其中每个滤镜都是一个独立的处理模块。例如,scale=1280:720滤镜改变分辨率,fps=30改变帧率,yadif执行去隔行处理。所有操作都在未压缩的帧上执行,因此第二阶段是最耗资源的。

第三阶段——编码为目标格式。处理后的帧被送入编码器,编码器根据目标编解码器的算法对其进行压缩。编码器可以是硬件编码器(iOS上的VideoToolbox、Android上的MediaCodec)或软件编码器(libx264、libx265)。编码参数:用于恒定质量的CRF(Constant Rate Factor)、用于CBR/VBR的比特率、用于目标设备兼容性的配置文件和级别。

cpp
// FFmpeg上的pipeline转码示意图
AVFormatContext *inputCtx, *outputCtx;
AVCodecContext *decoderCtx, *encoderCtx;
AVFrame *frame = av_frame_alloc();
AVPacket packet;

while (av_read_frame(inputCtx, &packet) >= 0) {
    // 1. 解码
    avcodec_send_packet(decoderCtx, &packet);
    avcodec_receive_frame(decoderCtx, frame);
    
    // 2. 滤镜(缩放 + fps变更)
    sws_scale(swsCtx, frame->data, frame->linesize,
              0, frame->height, scaledFrame->data,
              scaledFrame->linesize);
    
    // 3. 编码
    avcodec_send_frame(encoderCtx, scaledFrame);
    avcodec_receive_packet(encoderCtx, &outPacket);
    av_interleaved_write_frame(outputCtx, &outPacket);
}

上述pipeline展示了经典的转码循环。av_read_frame函数从输入文件中读取压缩数据包,avcodec_send_packet将其解码为帧,sws_scale执行缩放,avcodec_send_frame将处理后的帧编码为输出格式。这个三阶段循环对每个帧或帧组(GOP)重复,具体取决于编码器的设置。

转码 vs 转封装

转码和转封装之间的区别是媒体工程中最常见的混淆点之一。理解这一区别对于选择正确的媒体处理策略至关重要。

参数转码转封装
更改内容编解码器、比特率、分辨率容器、元数据
计算负载高(解码+编码)极低(复制数据包)
质量可能下降(代损失)无损
执行时间长视频需要数分钟到数小时数秒到数分钟
应用场景格式适配、压缩更改容器以实现兼容性

转封装——将压缩流重新打包到另一个容器中,无需解码和重新编码。如果视频已使用H.265编解码器压缩在MP4容器中,需要放入MOV或MKV容器——转封装只需将比特数据包从一个容器复制到另一个容器。质量不受影响,处理时间极短,因为不需要解码帧。FFmpeg使用-codec copy标志执行转封装。

转码则完全解压缩并重新压缩媒体流。每次视频经过转码时,可能会产生代损失(generation loss)——由于有损重复压缩而导致的质量轻微下降。即使在相同比特率下,第三代转码通常也比第一代差。这就是为什么专业人士建议以未压缩或最小压缩格式(ProRes、DNxHR)存储母版,并且只对最终交付版本进行转码。

转码工具

转码工具的选择取决于平台、性能要求和使用场景。移动开发既可以使用原生API,也可以使用跨平台库。

FFmpeg——通用工具

FFmpeg——所有平台上转码的事实标准。FFmpeg命令行几乎可以执行任何转换:更改编解码器、改变比特率、裁剪、拼接、叠加滤镜。对于移动应用,FFmpeg通过libavformat、libavcodec和libavfilter库集成。典型转码命令示例:ffmpeg -i input.mp4 -c:v libx265 -crf 23 -c:a aac -b:a 128k output.mp4

平台原生API

在iOS上,转码通过AVAssetWriterAVAssetReader执行。AVAssetReader通过读取未压缩帧来解码原始文件,而AVAssetWriter将其编码为目标格式。这种方法自动使用VideoToolbox硬件编码器,确保最高性能。在Android上,类似功能通过MediaCodec与MediaExtractor和MediaMuxer配合使用——MediaExtractor提取压缩数据包,MediaCodec解码和编码,MediaMuxer写入结果。

云服务

对于生产环境中的服务器端转码,使用云服务:AWS Elemental MediaConvert、Azure Media Services、Google Transcoder API。这些服务根据负载自动扩展,支持所有流行格式,可以将一个输入文件转码为数十种输出版本,用于自适应流媒体(HLS、DASH)。对于移动应用,云转码是最佳解决方案,因为它不会给用户设备带来负担,并且允许异步准备内容。

转码代码示例

让我们来看一下移动平台上使用硬件加速和关键质量参数配置的转码实践示例。

在iOS上将H.264转码为H.265

swift
import AVFoundation

func transcodeVideo(sourceURL: URL, destURL: URL) {
    let asset = AVAsset(url: sourceURL)
    let preset = AVAssetExportPresetHEVCHighestQuality
    
    AVAssetExportSession(asset: asset, presetName: preset)?
        .exportAsynchronously {
            switch assetExportSession?.status {
            case .completed:
                print("转码已完成")
            case .failed:
                print("错误: " + assetExportSession.error.localizedDescription)
            default:
                break
            }
        }
    
    // 使用AVAssetReader + AVAssetWriter手动转码
    let reader = try AVAssetReader(asset: asset)
    let writer = try AVAssetWriter(url: destURL,
        fileType: .mp4)
    
    let outputSettings: [String: Any] = [
        AVVideoCodecKey: AVVideoCodecType.hevc,
        AVVideoWidthKey: 1920,
        AVVideoHeightKey: 1080,
        AVVideoCompressionPropertiesKey: [
            AVVideoAverageBitRateKey: 4_000_000,
            AVVideoProfileLevelKey: AVVideoProfileLevelH265Main10
        ]
    ]
    
    let adaptor = AVAssetWriterInput(
        mediaType: .video,
        outputSettings: outputSettings
    )
    writer.add(adaptor)
}

该示例展示了在iOS上进行转码的两种方法。AVAssetExportSession是一种使用质量预设(用于H.265的HEVCHighestQuality)的简单方式。通过AVAssetReader + AVAssetWriter的手动pipeline提供了对参数(比特率、配置文件、级别)的完全控制。AVVideoProfileLevelH265Main10参数启用Main10 HDR配置文件(10位色深),这对于现代HDR内容非常重要。

在Android上使用MediaCodec进行转码

kotlin
class Transcoder(private val context: Context) {
    
    fun transcodeToHevc(inputUri: Uri, outputFile: File) {
        val extractor = MediaExtractor()
        extractor.setDataSource(context, inputUri, null)
        
        val trackFormat = extractor.getTrackFormat(videoTrackIndex)
        val mime = trackFormat.getString(MediaFormat.KEY_MIME)
        
        val decoder = MediaCodec.createDecoderByType(mime!!)
        val encoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_HEVC)
        
        val outputFormat = MediaFormat.createVideoFormat(
            MediaFormat.MIMETYPE_VIDEO_HEVC, 1920, 1080
        ).apply {
            setInteger(MediaFormat.KEY_BIT_RATE, 4_000_000)
            setInteger(MediaFormat.KEY_FRAME_RATE, 30)
            setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 2)
        }
        
        encoder.configure(outputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
        encoder.start()
    }
}

Android上的代码创建了一个由MediaExtractor——MediaCodec decoder——MediaCodec encoder——MediaMuxer组成的pipeline。MediaExtractor从输入文件中确定编解码器类型并选择相应的解码器。编码器设置为H.265(HEVC),比特率为4 Mbit/s,关键帧间隔为2秒,这对于流媒体来说是最佳的。重要提示:MediaCodec编码器同步工作,因此对于实时转码,需要组织一个循环,为每一帧正确处理时间戳(PTS)。

移动设备转码优化

由于CPU、GPU资源有限以及热限制,移动设备上的转码是一项需要仔细优化的任务。有几种策略可以帮助有效执行转码。

使用硬件编码

性能的关键因素——硬件编码器。在iOS上,VideoToolbox提供H.264和H.265硬件编码,速度比软件libx264快5-10倍。在Android上,MediaCodec使用硬件OMX组件(如果可用)。启用硬件编码可将10分钟视频的转码时间从30-40分钟(软件)减少到3-5分钟(硬件)——在旗舰设备上。

选择质量参数

对于移动转码,质量、大小和处理时间之间的平衡至关重要。对于移动设备上的H.265,建议1080p 30 FPS视频使用4-8 Mbit/s的比特率。CRF模式(Constant Rate Factor)在libx265中允许直接设置质量,23-28在文件大小适中的情况下提供良好的视觉质量。对于硬件编码器,请使用具有目标比特率的CBR模式,因为CRF不被硬件支持。

热量管理

在移动设备上持续进行转码会导致显著发热。经过5-7分钟的4K视频密集编码,处理器温度可能达到50-55度,之后会触发降频。解决方案——带暂停的转码或将帧率降低到30 FPS。如果应用需要批量转码(例如视频编辑器),最好以2-3分钟的批次进行处理,中间留有冷却间隔。对于生产场景,最好将转码转移到服务器端并使用云服务。

常见问题

转码和编码有什么区别?

编码是将原始未压缩数据压缩为目标编解码器。转码同时包括解码和编码:首先解码现有的压缩流,然后重新编码。简单编码接收未压缩数据(例如来自摄像头),而转码接收已压缩的文件。

移动设备转码应选择哪种编解码器?

为实现最大兼容性——选择H.264。为获得更好的压缩率——选择H.265(HEVC)。如果设备支持H.265硬件编码(iPhone 8+、搭载Snapdragon 845+的Android设备),它在相同质量下可提供大约小一倍的文件大小。AV1编码在移动设备上仍然太慢,即使有硬件加速也是如此。

什么是“无损转码”?

严格来说,在更换有损编解码器时,无损转码是不可能的。如果两个编解码器都是有损压缩,每一次转码代都会降低质量。无损转码仅可能发生在无损格式之间(FFV1、H.264 Lossless)或在不重新编码的情况下更改容器(转封装)。

可以实时转码视频吗?

可以,如果使用硬件解码器和编码器,且目标分辨率不超过1080p。在具有VideoToolbox(iOS)或MediaCodec(Android)的设备上,H.264→H.265的实时转码可以实现1-3秒的延迟。要实现4K实时转码,需要强大的SoC,如Apple A17 Pro、Snapdragon 8 Gen 2或更高版本。

为什么转码后视频质量变差了?

有损转码会累积压缩伪影。如果原始文件已经被高度压缩(1080p视频比特率2-3 Mbit/s),重复压缩将使损失加倍。建议仅从高比特率(20+ Mbit/s)的母版进行转码,并使用CRF 18-23以最大程度减少损失。

总结

  • Transcoding——更改编解码器、比特率或分辨率的完整媒体解码+编码周期
  • Transmuxing仅更改容器而无需重新编码——这不是转码
  • 硬件转码在VideoToolbox和MediaCodec上比软件快5-10倍
  • FFmpeg——支持数百种编解码器和处理滤镜的通用工具
  • 每一代有损转码都会降低质量——请使用母版副本
  • 移动H.265的最佳参数:比特率4-8 Mbit/s、Main10配置文件、GOP 2秒
  • 生产环境使用云转码服务(AWS Elemental、Azure Media)——它们不加重设备负担

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读