杰瑞科技汇

Android视频开发教程从哪开始学?

Android 视频开发全攻略

这份教程将分为以下几个核心模块:

Android视频开发教程从哪开始学?-图1
(图片来源网络,侵删)
  1. 基础篇:视频播放器开发
    • 使用 VideoView 快速实现
    • 使用 MediaPlayer + SurfaceView 完全控制
    • 使用现代框架 ExoPlayer(强烈推荐)
  2. 进阶篇:视频录制功能
    • 使用 MediaRecorder API
    • 录制流程与关键代码
  3. 实战篇:视频剪辑与处理
    • 使用 MovieMaker 进行简单拼接
    • 使用强大的第三方库 FFmpeg(Android 移植版)
  4. 高级篇:视频流媒体
    • 直播推流(RTMP)
    • 直播拉流(HLS, DASH)
  5. 学习资源与最佳实践

基础篇 - 视频播放器开发

视频播放是视频开发最常见的需求,Android 提供了多种方式,从简单易用到高度可控。

使用 VideoView (最简单)

VideoView 是一个封装了 MediaPlayerSurfaceView 的高级组件,适合快速实现播放功能。

布局文件 (activity_main.xml)

<VideoView
    android:id="@+id/videoView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_centerInParent="true" />

代码实现 (MainActivity.kt)

Android视频开发教程从哪开始学?-图2
(图片来源网络,侵删)
import android.net.Uri
import android.os.Bundle
import android.widget.MediaController
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
    private lateinit var videoView: VideoView
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        videoView = findViewById(R.id.videoView)
        // 1. 设置视频控制器(播放/暂停、进度条等)
        val mediaController = MediaController(this)
        mediaController.setAnchorView(videoView)
        videoView.setMediaController(mediaController)
        // 2. 设置视频源
        // 可以是本地资源文件
        // val videoUri = Uri.parse("android.resource://$packageName/${R.raw.sample_video}")
        // 也可以是网络URL
        val videoUri = Uri.parse("https://www.example.com/path/to/your/video.mp4")
        videoView.setVideoURI(videoUri)
        // 3. 设置准备和完成监听
        videoView.setOnPreparedListener { mp ->
            // 视频准备完成,可以开始播放
            mp?.start()
        }
        videoView.setOnCompletionListener {
            // 视频播放完成
            Toast.makeText(this, "播放完成", Toast.LENGTH_SHORT).show()
        }
        videoView.setOnErrorListener { mp, what, extra ->
            // 播放出错
            Toast.makeText(this, "播放出错: $what, $extra", Toast.LENGTH_SHORT).show()
            true
        }
    }
}

优点: 简单快捷,几行代码就能搞定。 缺点: 功能有限,自定义性差,性能和兼容性不如 ExoPlayer


使用 MediaPlayer + SurfaceView (完全控制)

这种方式需要你手动管理播放器生命周期和渲染,但提供了最大的灵活性。

布局文件 (activity_main.xml)

<SurfaceView
    android:id="@+id/surfaceView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

代码实现 (MainActivity.kt)

Android视频开发教程从哪开始学?-图3
(图片来源网络,侵删)
import android.media.MediaPlayer
import android.net.Uri
import android.os.Bundle
import android.view.SurfaceHolder
import android.view.SurfaceView
import androidx.appcompat.app.AppCompatActivity
class MediaPlayerActivity : AppCompatActivity(), SurfaceHolder.Callback {
    private lateinit var surfaceView: SurfaceView
    private lateinit var surfaceHolder: SurfaceHolder
    private var mediaPlayer: MediaPlayer? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        surfaceView = findViewById(R.id.surfaceView)
        surfaceHolder = surfaceView.holder
        surfaceHolder.addCallback(this)
        // 注意:MediaPlayer的创建和设置应该在Surface准备好之后
    }
    override fun surfaceCreated(holder: SurfaceHolder) {
        // Surface已创建,可以初始化MediaPlayer
        mediaPlayer = MediaPlayer().apply {
            // 设置显示的Surface
            setDisplay(holder)
            // 设置数据源
            val videoUri = Uri.parse("https://www.example.com/path/to/your/video.mp4")
            setDataSource(this@MediaPlayerActivity, videoUri)
            // 异步准备
            prepareAsync()
            setOnPreparedListener { mp ->
                mp.start()
            }
        }
    }
    override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
        // Surface尺寸改变时调用
    }
    override fun surfaceDestroyed(holder: SurfaceHolder) {
        // Surface被销毁,必须释放MediaPlayer资源
        mediaPlayer?.release()
        mediaPlayer = null
    }
    override fun onDestroy() {
        super.onDestroy()
        // 确保在Activity销毁时释放资源
        mediaPlayer?.release()
        mediaPlayer = null
    }
}

优点: 完全控制播放过程,适合需要自定义渲染逻辑的场景。 缺点: 代码复杂,需要手动处理资源释放,容易出错。


使用 ExoPlayer (现代、强大、推荐)

Google 官方推出的媒体播放框架,是目前 Android 视频播放的首选方案,它支持多种格式、自适应码率流、DRM 加密等高级功能,并且性能极佳。

添加依赖 (app/build.gradle)

dependencies {
    implementation "androidx.media3:media3-exoplayer:1.3.1" // 核心库
    implementation "androidx.media3:media3-ui:1.3.1"     // UI组件 (PlayerView)
    implementation "androidx.media3:media3-exoplayer-hls:1.3.1" // HLS支持
    implementation "androidx.media3:media3-exoplayer-dash:1.3.1" // DASH支持
}

布局文件 (activity_main.xml)

<androidx.media3.ui.PlayerView
    android:id="@+id/playerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:use_controller="true" /> <!-- 显示控制器 -->

代码实现 (MainActivity.kt)

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView
class ExoPlayerActivity : AppCompatActivity() {
    private lateinit var playerView: PlayerView
    private var exoPlayer: ExoPlayer? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        playerView = findViewById(R.id.playerView)
        // 1. 创建ExoPlayer实例
        exoPlayer = ExoPlayer.Builder(this).build()
        // 2. 将播放器与PlayerView关联
        playerView.player = exoPlayer
        // 3. 准备并播放媒体
        val mediaItem = MediaItem.fromUri("https://www.example.com/path/to/your/video.mp4")
        exoPlayer?.setMediaItem(mediaItem)
        exoPlayer?.prepare()
        exoPlayer?.playWhenReady = true // 自动播放
    }
    override fun onStop() {
        super.onStop()
        // 在后台时暂停播放,节省资源
        exoPlayer?.playWhenReady = false
    }
    override fun onDestroy() {
        super.onDestroy()
        // 释放播放器资源
        exoPlayer?.release()
        exoPlayer = null
    }
}

优点:

  • 功能强大:支持几乎所有现代媒体格式和流媒体协议。
  • 性能优越:硬件加速,自适应码率,缓冲策略智能。
  • 高度可定制:UI 和播放逻辑都可以深度定制。
  • 官方维护:由 Google 维护,稳定性和未来有保障。
  • 生命周期管理简单:与 Lifecycle 组件集成良好。

缺点: API 相对前两者更复杂,学习成本稍高。


进阶篇 - 视频录制功能

使用 MediaRecorder API 可以实现视频录制。

核心步骤:

  1. 获取 CameraX 或 Camera2 的 Surface (用于预览和录制)。
  2. 配置 MediaRecorder:设置输出文件格式、编码器、视频/音频源等。
  3. 准备、开始、停止、释放

代码示例 (简化版):

import android.media.MediaRecorder
import android.os.Bundle
import android.view.Surface
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import java.io.File
class RecordActivity : AppCompatActivity() {
    private lateinit var mediaRecorder: MediaRecorder
    private lateinit var output: File
    private var isRecording = false
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_record)
        val recordButton: Button = findViewById(R.id.record_button)
        // 假设你已经从Camera2/CameraX获取到了previewSurface
        val previewSurface: Surface = ... 
        output = File(getExternalFilesDir(null), "recorded_video.mp4")
        recordButton.setOnClickListener {
            if (!isRecording) {
                startRecording(previewSurface)
                recordButton.text = "停止"
            } else {
                stopRecording()
                recordButton.text = "开始"
            }
        }
    }
    private fun startRecording(surface: Surface) {
        mediaRecorder = MediaRecorder().apply {
            setVideoSource(MediaRecorder.VideoSource.SURFACE)
            setAudioSource(MediaRecorder.AudioSource.MIC)
            setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
            setOutputFile(output.absolutePath)
            setVideoEncoder(MediaRecorder.VideoEncoder.H264)
            setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
            setVideoSize(1920, 1080) // 设置分辨率
            setVideoEncodingBitRate(5_000_000) // 设置比特率
            setPreviewDisplay(surface) // 设置预览Surface
            prepare()
            start()
        }
        isRecording = true
    }
    private fun stopRecording() {
        mediaRecorder.apply {
            stop()
            reset()
            release()
        }
        isRecording = false
        // 录制完成,文件保存在 output.path
    }
}

重要提示:

  • 权限: 必须在 AndroidManifest.xml 中添加 CAMERARECORD_AUDIO 权限,并在运行时动态请求。
  • Camera2 API: 现代应用推荐使用 Camera2 API,它比旧的 Camera API 更强大,但也更复杂。CameraX 是对 Camera2 的封装,可以简化开发。
  • 生命周期: MediaRecorder 的创建、准备、释放必须在正确的生命周期回调中处理,避免内存泄漏和崩溃。

实战篇 - 视频剪辑与处理

Android 原生 API 对视频剪辑的支持非常有限,通常需要借助第三方库。

使用 MovieMaker (拼接)

MediaCodec API 中的 MediaMuxer 可以将编码后的音视频轨道合成为一个文件,这可以实现简单的拼接(前提是视频格式、分辨率、编码器完全相同)。

流程:

  1. 使用 MediaExtractor 分别从两个视频中分离出视频轨道和音频轨道。
  2. 使用 MediaCodec 解码这些轨道(或者直接复用已编码的数据)。
  3. 使用 MediaMuxer 将轨道A的视频和轨道B的视频(以及音频)写入到一个新的输出文件中。

这种方法非常底层,代码量巨大,不推荐新手尝试。

使用第三方库 (推荐)

FFmpeg (Android 移植版)

FFmpeg 是一个强大的音视频处理命令行工具,在 Android 上,我们可以使用它的移植版,如 FFmpegKitandroid-ffmpeg

添加依赖 (以 FFmpegKit 为例)

// 在 project/build.gradle 的 repositories 中添加
maven {
    url 'https://jitpack.io'
}
// 在 app/build.gradle 的 dependencies 中添加
implementation 'com.github.arthenica:ffmpeg-kit-android:6.0-essentials-lite' // lite版本包含常用功能

使用 FFmpeg 命令进行剪辑

import io.ffmpeg.kotlin.FFmpegKit
class EditActivity : AppCompatActivity() {
    fun trimVideo(inputPath: String, outputPath: String, startTime: String, duration: String) {
        // FFmpeg命令: ffmpeg -i input.mp4 -ss 00:00:10 -t 00:00:05 -c copy output.mp4
        val command = arrayOf(
            "-i", inputPath,
            "-ss", startTime, // 开始时间
            "-t", duration,  // 持续时间
            "-c", "copy",     // 直接复制流,不重新编码,速度快
            outputPath
        )
        // 执行命令
        val session = FFmpegKit.execute(command.joinToString(" "))
        if (session.returnCode == 0) {
            // 剪辑成功
            Log.d("FFmpeg", "剪辑成功: ${session.output}")
        } else {
            // 剪辑失败
            Log.e("FFmpeg", "剪辑失败: ${session.failStackTrace}")
        }
    }
}

优点: 功能极其强大,几乎可以实现所有音视频处理操作(剪辑、合并、转码、添加滤镜等)。 缺点: 需要学习 FFmpeg 命令,集成和部署库文件(如 .so 文件)相对复杂。


高级篇 - 视频流媒体

直播推流

将手机摄像头捕获的视频和麦克风的声音,实时推送到 RTMP 服务器。

  • 技术栈:
    • 采集: Camera2 API / AudioRecord
    • 编码: MediaCodec (H.264 视频编码, AAC 音频编码)
    • 封装: 将编码后的数据打包成 FLV 格式
    • 推送: 使用 RTMP 协议通过 Socket 推送到服务器
  • 库推荐: rtmp-rtplibrary (如 bytedance/rtmp-rtplibrary),封装了底层细节,简化了推流实现。

直播拉流

从 RTMP 服务器拉取直播流,并进行播放。

  • 技术栈:
    • 协议: RTMP (主协议), HLS (延迟较高,但兼容性好), DASH
    • 播放: 直接使用 ExoPlayer ExoPlayer 对这些协议有非常好的支持。
  • 实现:
    // 使用ExoPlayer拉取RTMP流
    val mediaItem = MediaItem.fromUri("rtmp://live.example.com/live/stream_key")
    exoPlayer?.setMediaItem(mediaItem)
    exoPlayer?.prepare()

学习资源与最佳实践

学习资源

  1. 官方文档 (必看):

  2. GitHub 项目:

  3. 博客与教程:

    • ProAndroidDev: 上面有很多高质量的音视频开发文章。
    • 掘金、CSDN: 搜索 "Android ExoPlayer", "Android MediaRecorder" 等关键词,有大量中文教程。

最佳实践

  1. 优先使用 ExoPlayer: 除非你的项目极其简单,否则都应该将 ExoPlayer 作为视频播放的首选。
  2. 生命周期管理: 始终在 onPause() / onStop() 中暂停播放器,在 onDestroy() 中释放资源,避免内存泄漏和后台播放。
  3. 处理异常: 网络中断、格式不支持、解码失败等情况是常态,代码中必须有完善的错误处理和重试逻辑。
  4. 性能优化:
    • 对于高清视频,优先使用硬件解码。
    • 使用自适应码率流,根据网络状况自动调整清晰度。
    • 避免在主线程中执行耗时操作(如视频解码、文件读写)。
  5. 权限处理: 务必在运行时动态请求相机和录音权限,并处理用户拒绝的情况。
  6. 考虑第三方库: 对于复杂的推流、剪辑等功能,不要重复造轮子,选择成熟稳定的第三方库可以大大提高开发效率和项目稳定性。

希望这份详细的教程能帮助你顺利开启 Android 视频开发之旅!

分享:
扫描分享到社交APP
上一篇
下一篇