Giảm bớt khó khăn biên dịch: Tích hợp AeroFFmpeg cho Android
Giới thiệu
Bạn có phải là một lập trình viên Android đang tìm kiếm giải pháp để tích hợp các tính năng âm thanh và video vào ứng dụng của mình mà không muốn đối mặt với quá trình biên dịch phức tạp của FFmpeg? Nếu có, bạn không đơn độc! Nhiều lập trình viên cảm thấy bối rối khi phải:
- Tải xuống mã nguồn FFmpeg cùng với nhiều thư viện phụ thuộc khác.
- Cấu hình môi trường biên dịch chéo phức tạp như NDK và trình biên dịch.
- Viết các kịch bản biên dịch dài dòng để xử lý các vấn đề tương thích với các kiến trúc CPU và hệ điều hành khác nhau.
- Phải bắt đầu lại từ đầu mỗi khi có bản cập nhật mới của FFmpeg.
Tất cả những điều này đều có thể khiến bạn cảm thấy mệt mỏi và mất thời gian. Nhưng đừng lo, bài viết này sẽ giới thiệu về AeroFFmpeg, một dự án SDK FFmpeg mã nguồn mở cho Android. AeroFFmpeg biên dịch FFmpeg thành tệp AAR dễ sử dụng, giúp các lập trình viên Android nhanh chóng tích hợp các tính năng mạnh mẽ của FFmpeg vào trong dự án của mình chỉ với vài dòng mã.
Tích hợp AeroFFmpeg một cách đơn giản
AeroFFmpeg hiện tại có minSdk là 24 và targetSdk là 34. Nó được biên dịch dựa trên FFmpeg 4.2.9 và hỗ trợ các định dạng như MP3, H.264 và H.265. Tuy nhiên, AeroFFmpeg hiện chỉ hỗ trợ các kiến trúc arm64-v8a và x86, chưa hỗ trợ kích thước trang 16KB. Khi đã tích hợp AeroFFmpeg vào dự án của mình, bạn có thể ngay lập tức sử dụng các lệnh FFmpeg.
Nhập tệp AAR của AeroFFmpeg
Vì AeroFFmpeg chưa được phát hành trên kho Maven, bạn sẽ cần tích hợp tệp AAR trực tiếp bằng cách nhập nó. Địa chỉ tệp AAR như sau:
https://github.com/Ilovecat1949/AeroFFmpeg/releases/tag/v1.0.0
-
Sao chép tệp AAR vào thư mục libs dưới module app trong dự án Android của bạn. Nếu thư mục libs chưa tồn tại, hãy tạo một thư mục mới.
-
Cấu hình tệp build.gradle. Mở tệp build.gradle của module app và cấu hình hai phần: repositories và dependencies.
gradle
repositories {
flatDir {
dirs("libs")
}
}
implementation(files("libs/AeroFFmpeglib-release.aar"))
Sau khi cấu hình, hãy đảm bảo rằng bạn đã loại bỏ dòng repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
trong tệp settings.gradle.kts, nếu có, hãy thay thế bằng repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
.
- Nhấn nút Sync Now để đồng bộ dự án. Gradle sẽ tự động tải xuống tệp .aar và tất cả các phụ thuộc từ kho Maven đã chỉ định và thêm vào dự án của bạn.
Với các bước trên, bạn đã tích hợp thành công AeroFFmpeg vào dự án của mình.
Sử dụng AeroFFmpeg và các tình huống ứng dụng
AeroFFmpeg sử dụng mô hình singleton và hỗ trợ dòng lệnh FFmpeg, thực thi không đồng bộ trong một luồng con. Lưu ý rằng AeroFFmpeg không hỗ trợ thực thi dòng lệnh FFmpeg đồng thời; một tác vụ dòng lệnh mới không thể bắt đầu trước khi tác vụ trước đó hoàn thành. Nếu bạn cần hỗ trợ nhiều tác vụ âm thanh và video đồng thời, hãy xem xét việc sử dụng lập trình đa tiến trình trên Android để gọi các phiên bản AeroFFmpeg khác nhau.
kotlin
// Bắt đầu một tác vụ dòng lệnh FFmpeg
// Trả về kết quả: 0 cho thành công, -1 cho thất bại
val result = AeroFFmpeg.start("ffmpeg -i input.mp4 output.mp4")
// Lấy trạng thái hiện tại của tác vụ: 0: Đang thực hiện tác vụ hoặc không thực hiện gì cả; 1: Tác vụ hoàn thành không có lỗi; -1: Tác vụ hoàn thành với lỗi
AeroFFmpeg.getTaskState()
// Hủy tác vụ
AeroFFmpeg.cancelTask()
// Lấy tiến độ hiện tại của tác vụ. Trả về một giá trị Double tính bằng mili giây.
AeroFFmpeg.getProgressTime()
// Xử lý thông tin log thông qua hàm callback log
AeroFFmpeg.setLogListener (
object: OnLogListener {
override fun onLog(level: Int, message: String) {
Log.d("AeroFFmpeg.setLogListener","$level,$message")
}
}
)
Hoàn thành các tác vụ âm thanh và video nền với AeroFFmpeg và WorkManager
Tiếp theo, chúng ta sẽ phát triển một ứng dụng thực hiện các tác vụ âm thanh và video nền dựa trên AeroFFmpeg và WorkManager. WorkManager, được khuyến nghị chính thức cho các tác vụ nền, mang lại nhiều ưu điểm như độ tin cậy cao, thực thi liên tục và hiệu suất năng lượng tốt. Để tìm hiểu chi tiết về WorkManager, hãy tham khảo bài viết trước của tôi.
Trước tiên, định nghĩa một lớp Worker để thực hiện dòng lệnh FFmpeg.
kotlin
class FFmpegTaskWorker(
appContext: Context,
workerParams: WorkerParameters
) : CoroutineWorker(appContext, workerParams) {
// Override getForegroundInfo() để cung cấp thông tin thông báo dịch vụ nền
override suspend fun getForegroundInfo(): ForegroundInfo {
return ForegroundInfo(
FFMPEG_CMD_ID,
notificationBuilder.setContentText("Bắt đầu thực hiện tác vụ...").build()
)
}
override suspend fun doWork(): Result {
setForeground(getForegroundInfo())
val cmd = inputData.getString(FFMPEG_CMD)
return withContext(Dispatchers.IO) {
try {
if(AeroFFmpeg.start(cmd!!)<0){
return@withContext Result.failure()
}
while(AeroFFmpeg.getTaskState()==0){
notificationBuilder.setContentText("Tiến độ tác vụ: ${formatTimeFromMillis(AeroFFmpeg.getProgressTime())}")
setProgress(AeroFFmpeg.getProgressTime())
delay(100)
}
if(AeroFFmpeg.getTaskState()==1){
notificationBuilder.setContentText("Thực hiện thành công")
return@withContext Result.success()
}
else{
notificationBuilder.setContentText("Thực hiện thất bại")
return@withContext Result.failure()
}
} catch (e: Exception) {
e.printStackTrace()
// Tác vụ thất bại, cập nhật thông báo
notificationBuilder.setContentText("Thực hiện thất bại").setProgress(0, 0, false)
return@withContext Result.failure()
} finally {
// Đảm bảo thông báo bị hủy
notificationManager.cancel(FFMPEG_CMD_ID)
}
}
}
}
Tiếp theo, định nghĩa một phương thức để bắt đầu tác vụ và đóng gói quá trình gửi tác vụ cho WorkManager.
kotlin
fun startFFmpegTask(context: Context, cmd: String) {
val data = workDataOf(FFMPEG_CMD to cmd)
val constraints = Constraints.Builder()
.build()
val ffmpegTaskRequest = OneTimeWorkRequestBuilder<FFmpegTaskWorker>()
.setConstraints(constraints)
.setInputData(data)
.addTag(FFMPEG_TASK_TAG)
.build()
WorkManager.getInstance(context).enqueue(ffmpegTaskRequest)
}
Thông qua lớp LiveData, trạng thái và tiến độ của các tác vụ có thể được quan sát và hiển thị theo thời gian thực, đạt được sự đồng bộ giữa thông tin tác vụ nền và thông tin hiển thị UI.
kotlin
val context = LocalContext.current
val workManager = WorkManager.getInstance(context)
var cmd by remember { mutableStateOf("") }
val workInfos: LiveData<List<WorkInfo>> = remember {
workManager.getWorkInfosByTagLiveData(FFMPEG_TASK_TAG)
}
val ffmpegTasks by workInfos.observeAsState(initial = emptyList())
...
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
items(ffmpegTasks) { workInfo ->
FFmpegTaskItem(workInfo)
}
}
Kết luận
AeroFFmpeg giúp bạn dễ dàng triển khai các tính năng âm thanh và video trong ứng dụng Android mà không cần cấu hình NDK phức tạp hay viết các kịch bản biên dịch dài dòng. FFmpeg là một công cụ xử lý âm thanh và video mã nguồn mở mạnh mẽ, hỗ trợ nhiều tính năng như chuyển đổi, chỉnh sửa, ghi âm, phát trực tuyến và lọc. Tôi sẽ tiếp tục cập nhật và cải thiện AeroFFmpeg.
Để tìm hiểu thêm về AeroFFmpeg, hãy truy cập kho lưu trữ sau:
https://github.com/Ilovecat1949/AeroFFmpeg
Mã mẫu AeroFFmpeg + WorkManager có trong dự án WorkDownloader tại kho sau:
https://github.com/Ilovecat1949/AndroidApps
Các thực tiễn tốt nhất
- Luôn kiểm tra các quyền cần thiết trước khi thực hiện tác vụ âm thanh và video.
- Sử dụng các thông báo để thông báo cho người dùng về trạng thái tác vụ.
Các cạm bẫy thường gặp
- Không xử lý các lỗi trong quá trình thực hiện có thể dẫn đến ứng dụng bị treo.
- Bỏ qua việc kiểm tra trạng thái của tác vụ trước khi bắt đầu tác vụ mới.
Mẹo tối ưu hiệu suất
- Sử dụng các luồng nền để tránh làm chậm UI trong quá trình xử lý.
- Tối ưu hóa các lệnh FFmpeg để giảm thiểu thời gian xử lý.
Phần hỏi đáp (FAQ)
Câu hỏi 1: AeroFFmpeg có hỗ trợ những định dạng nào?
AeroFFmpeg hỗ trợ các định dạng như MP3, H.264 và H.265.
Câu hỏi 2: Có thể sử dụng AeroFFmpeg trên các kiến trúc nào?
Hiện tại, AeroFFmpeg chỉ hỗ trợ arm64-v8a và x86.
Câu hỏi 3: Tôi có thể chạy nhiều tác vụ FFmpeg đồng thời không?
Không, AeroFFmpeg không hỗ trợ thực thi đồng thời các tác vụ FFmpeg. Hãy sử dụng lập trình đa tiến trình nếu cần hỗ trợ nhiều tác vụ.