[英]FFmpeg on Android
我在 Android 上編譯了 FFmpeg (libffmpeg.so)。 現在我必須構建一個像 RockPlayer 這樣的應用程序,或者使用現有的 Android 多媒體框架來調用 FFmpeg。
您是否有在 Android / StageFright 上集成 FFmpeg 的步驟/程序/代碼/示例?
你能指導我如何使用這個庫進行多媒體播放嗎?
我有一個要求,我已經有音頻和視頻傳輸流,我需要將其提供給 FFmpeg 並對其進行解碼/渲染。 我如何在 Android 上執行此操作,因為 IOMX API 是基於 OMX 的並且不能在此處插入 FFmpeg?
我也找不到有關需要用於播放的 FFmpeg API 的文檔。
以下是我讓 ffmpeg 在 Android 上運行所經歷的步驟:
make
之遙。 您還需要從 Android 構建中提取 bionic(libc) 和 zlib(libz),因為 ffmpeg 庫依賴於它們。 使用 Android NDK創建一個包裝 ffmpeg 功能的動態庫。 有很多關於如何使用 NDK 的文檔。 基本上,您需要編寫一些 C/C++ 代碼來將您需要的功能從 ffmpeg 導出到 java 可以通過 JNI 與之交互的庫中。 NDK 允許您輕松鏈接到您在第 1 步中生成的靜態庫,只需在 Android.mk 中添加與此類似的行: LOCAL_STATIC_LIBRARIES := libavcodec libavformat libavutil libc libz
使用 java 源中的 ffmpeg-wrapping 動態庫。 那里有足夠的關於 JNI 的文檔,你應該沒問題。
關於使用ffmpeg進行播放,例子很多(ffmpeg二進制本身就是一個很好的例子),這里有一個基礎教程。 最好的文檔可以在標題中找到。
祝你好運 :)
由於各種原因,多媒體在不影響效率的情況下完成任務從來都不是一件容易的事。 ffmpeg 每天都在努力改進它。 它支持不同格式的編解碼器和容器。
現在要回答如何使用這個庫的問題,我會說在這里寫它並不是那么簡單。 但我可以通過以下方式指導你。
1) 在源代碼的 ffmpeg 目錄中,您有output_example.c或api_example.c 。 在這里,您可以看到完成編碼/解碼的代碼。 您將了解應該調用 ffmpeg 中的哪個 API。 這將是你的第一步。
2) Dolphin player 是Android 的開源項目。 目前它有錯誤,但開發人員正在持續工作。 在該項目中,您已准備好可用於繼續調查的整個設置。 這是來自 code.google.com 的項目鏈接或在終端中運行命令“ git clone https://code.google.com/p/dolphin-player/ ”。 您可以看到兩個名為 P 和 P86 的項目。 您可以使用其中任何一個。
我想提供的額外提示是,當您構建 ffmpeg 代碼時,在 build.sh 中,您需要啟用要使用的格式的復用器/解復用器/編碼器/解碼器。 否則相應的代碼將不會包含在庫中。 我花了很多時間才意識到這一點。 於是想到與大家分享。
一些基礎知識:當我們說一個視頻文件時,例如:avi,它是音頻和視頻的組合
視頻文件 = 視頻 + 音頻
視頻 = 編解碼器 + 多路復用器 + 多路分離器
編解碼器 = 編碼器 + 解碼器
=>視頻 = 編碼器 + 解碼器 + 復用器 + 解復用器(Mpeg4 + Mpeg4 + avi +avi - avi 容器示例)
音頻 = 編解碼器 + 多路復用器 + 多路分離器
編解碼器 = 編碼器 + 解碼器
=>音頻 = 編碼器 + 解碼器 + Muxer + Demuxer(mp2 + mp2 + avi + avi - avi 容器示例)
編解碼器(名稱源自 en*co*der/*dec*oder 的組合)只是定義用於編碼/解碼幀的算法的格式的一部分。 AVI 不是編解碼器,它是一個容器,它使用 Mpeg4 的視頻編解碼器和 mp2 的音頻編解碼器。
Muxer/demuxer 用於從編碼/解碼時使用的文件中組合/分離幀。
所以如果要使用avi格式,需要開啟Video組件+Audio組件。
例如,對於 avi,您需要啟用以下功能。 mpeg4 編碼器、mpeg4 解碼器、mp2 編碼器、mp2 解碼器、avi 復用器、avi 解復用器。
嗚嗚嗚嗚...
以編程方式 build.sh 應包含以下代碼:
--enable-muxer=avi --enable-demuxer=avi (Generic for both audio/video. generally Specific to a container)
--enable-encoder=mpeg4 --enable-decoder=mpeg4(For video support)
--enable-encoder=mp2 --enable-decoder=mp2 (For Audio support)
希望在這一切之后我沒有讓你更加困惑......
謝謝,需要任何幫助,請告訴我。
經過大量研究,現在這是我發現的最新的 Android 編譯庫:
https://github.com/bravobit/FFmpeg-Android
FFmpeg release n4.0-39-gda39990
我發現的最容易構建、最容易使用的實現是由監護人項目團隊制作的: https : //github.com/guardianproject/android-ffmpeg
我已經完成了一個使用 Android NDK 配置和構建 X264 和 FFMPEG 的小項目。 缺少的主要內容是一個體面的 JNI 接口,使其可以通過 Java 訪問,但這是簡單的部分(相對而言)。 當我開始着手使 JNI 接口適合我自己的用途時,我會將其推入。
olvaffe 構建系統的優勢在於它不需要 Android.mk 文件來構建庫,它只使用常規的 makefile 和工具鏈。 當您從 FFMPEG 或 X264 中提取新更改時,這使得停止工作的可能性大大降低。
為了制作我的 FFMPEG 應用程序,我使用了這個項目( https://github.com/hiteshsondhi88/ffmpeg-android-java ),所以我不需要編譯任何東西。 我認為這是在我們的 Android 應用程序中使用 FFMPEG 的簡單方法。
有關http://hiteshsondhi88.github.io/ffmpeg-android-java/ 的更多信息
受到 Android 上許多其他 FFmpeg 實現(主要是guadianproject )的啟發,我找到了一個解決方案(也有 Lame 支持)。
(跛腳和 FFmpeg: https : //github.com/intervigilium/liblame和http://bambuser.com/opensource )
調用 FFmpeg:
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
FfmpegController ffmpeg = null;
try {
ffmpeg = new FfmpegController(context);
} catch (IOException ioe) {
Log.e(DEBUG_TAG, "Error loading ffmpeg. " + ioe.getMessage());
}
ShellDummy shell = new ShellDummy();
String mp3BitRate = "192";
try {
ffmpeg.extractAudio(in, out, audio, mp3BitRate, shell);
} catch (IOException e) {
Log.e(DEBUG_TAG, "IOException running ffmpeg" + e.getMessage());
} catch (InterruptedException e) {
Log.e(DEBUG_TAG, "InterruptedException running ffmpeg" + e.getMessage());
}
Looper.loop();
}
}).start();
並處理控制台輸出:
private class ShellDummy implements ShellCallback {
@Override
public void shellOut(String shellLine) {
if (someCondition) {
doSomething(shellLine);
}
Utils.logger("d", shellLine, DEBUG_TAG);
}
@Override
public void processComplete(int exitValue) {
if (exitValue == 0) {
// Audio job OK, do your stuff:
// i.e.
// write id3 tags,
// calls the media scanner,
// etc.
}
}
@Override
public void processNotStartedCheck(boolean started) {
if (!started) {
// Audio job error, as above.
}
}
}
奇怪的是這個項目沒有被提及:來自 Appunite 的 AndroidFFmpeg
它有非常詳細的分步說明來復制/粘貼到命令行,適合像我這樣的懶人))
我有同樣的問題,我發現這里的大部分答案都過時了。 我最終在 FFMPEG 上編寫了一個包裝器,以便使用一行代碼從 Android 訪問。
一、添加FFmpeg庫的依賴
implementation 'com.writingminds:FFmpegAndroid:0.3.2'
然后加載活動
FFmpeg ffmpeg;
private void trimVideo(ProgressDialog progressDialog) {
outputAudioMux = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES).getAbsolutePath()
+ "/VidEffectsFilter" + "/" + new SimpleDateFormat("ddMMyyyy_HHmmss").format(new Date())
+ "filter_apply.mp4";
if (startTrim.equals("")) {
startTrim = "00:00:00";
}
if (endTrim.equals("")) {
endTrim = timeTrim(player.getDuration());
}
String[] cmd = new String[]{"-ss", startTrim + ".00", "-t", endTrim + ".00", "-noaccurate_seek", "-i", videoPath, "-codec", "copy", "-avoid_negative_ts", "1", outputAudioMux};
execFFmpegBinary1(cmd, progressDialog);
}
private void execFFmpegBinary1(final String[] command, ProgressDialog prpg) {
ProgressDialog progressDialog = prpg;
try {
ffmpeg.execute(command, new ExecuteBinaryResponseHandler() {
@Override
public void onFailure(String s) {
progressDialog.dismiss();
Toast.makeText(PlayerTestActivity.this, "Fail to generate video", Toast.LENGTH_SHORT).show();
Log.d(TAG, "FAILED with output : " + s);
}
@Override
public void onSuccess(String s) {
Log.d(TAG, "SUCCESS wgith output : " + s);
// pathVideo = outputAudioMux;
String finalPath = outputAudioMux;
videoPath = outputAudioMux;
Toast.makeText(PlayerTestActivity.this, "Storage Path =" + finalPath, Toast.LENGTH_SHORT).show();
Intent intent = new Intent(PlayerTestActivity.this, ShareVideoActivity.class);
intent.putExtra("pathGPU", finalPath);
startActivity(intent);
finish();
MediaScannerConnection.scanFile(PlayerTestActivity.this, new String[]{finalPath}, new String[]{"mp4"}, null);
}
@Override
public void onProgress(String s) {
Log.d(TAG, "Started gcommand : ffmpeg " + command);
progressDialog.setMessage("Please Wait video triming...");
}
@Override
public void onStart() {
Log.d(TAG, "Startedf command : ffmpeg " + command);
}
@Override
public void onFinish() {
Log.d(TAG, "Finished f command : ffmpeg " + command);
progressDialog.dismiss();
}
});
} catch (FFmpegCommandAlreadyRunningException e) {
// do nothing for now
}
}
private void loadFFMpegBinary() {
try {
if (ffmpeg == null) {
ffmpeg = FFmpeg.getInstance(this);
}
ffmpeg.loadBinary(new LoadBinaryResponseHandler() {
@Override
public void onFailure() {
showUnsupportedExceptionDialog();
}
@Override
public void onSuccess() {
Log.d("dd", "ffmpeg : correct Loaded");
}
});
} catch (FFmpegNotSupportedException e) {
showUnsupportedExceptionDialog();
} catch (Exception e) {
Log.d("dd", "EXception no controlada : " + e);
}
}
private void showUnsupportedExceptionDialog() {
new AlertDialog.Builder(this)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle("Not Supported")
.setMessage("Device Not Supported")
.setCancelable(false)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
})
.create()
.show();
}
public String timeTrim(long milliseconds) {
String finalTimerString = "";
String minutString = "";
String secondsString = "";
// Convert total duration into time
int hours = (int) (milliseconds / (1000 * 60 * 60));
int minutes = (int) (milliseconds % (1000 * 60 * 60)) / (1000 * 60);
int seconds = (int) ((milliseconds % (1000 * 60 * 60)) % (1000 * 60) / 1000);
// Add hours if there
if (hours < 10) {
finalTimerString = "0" + hours + ":";
} else {
finalTimerString = hours + ":";
}
if (minutes < 10) {
minutString = "0" + minutes;
} else {
minutString = "" + minutes;
}
// Prepending 0 to seconds if it is one digit
if (seconds < 10) {
secondsString = "0" + seconds;
} else {
secondsString = "" + seconds;
}
finalTimerString = finalTimerString + minutString + ":" + secondsString;
// return timer string
return finalTimerString;
}
還使用 FFmpeg 的另一個功能
===> merge audio to video
String[] cmd = new String[]{"-i", yourRealPath, "-i", arrayList.get(posmusic).getPath(), "-map", "1:a", "-map", "0:v", "-codec", "copy", "-shortest", outputcrop};
===> Flip vertical :
String[] cm = new String[]{"-i", yourRealPath, "-vf", "vflip", "-codec:v", "libx264", "-preset", "ultrafast", "-codec:a", "copy", outputcrop1};
===> Flip horizontally :
String[] cm = new String[]{"-i", yourRealPath, "-vf", "hflip", "-codec:v", "libx264", "-preset", "ultrafast", "-codec:a", "copy", outputcrop1};
===> Rotate 90 degrees clockwise:
String[] cm=new String[]{"-i", yourRealPath, "-c", "copy", "-metadata:s:v:0", "rotate=90", outputcrop1};
===> Compress Video
String[] complexCommand = {"-y", "-i", yourRealPath, "-strict", "experimental", "-vcodec", "libx264", "-preset", "ultrafast", "-crf", "24", "-acodec", "aac", "-ar", "22050", "-ac", "2", "-b", "360k", "-s", "1280x720", outputcrop1};
===> Speed up down video
String[] complexCommand = {"-y", "-i", yourRealPath, "-filter_complex", "[0:v]setpts=2.0*PTS[v];[0:a]atempo=0.5[a]", "-map", "[v]", "-map", "[a]", "-b:v", "2097k", "-r", "60", "-vcodec", "mpeg4", outputcrop1};
String[] complexCommand = {"-y", "-i", yourRealPath, "-filter_complex", "[0:v]setpts=1.0*PTS[v];[0:a]atempo=1.0[a]", "-map", "[v]", "-map", "[a]", "-b:v", "2097k", "-r", "60", "-vcodec", "mpeg4", outputcrop1};
String[] complexCommand = {"-y", "-i", yourRealPath, "-filter_complex", "[0:v]setpts=0.75*PTS[v];[0:a]atempo=1.5[a]", "-map", "[v]", "-map", "[a]", "-b:v", "2097k", "-r", "60", "-vcodec", "mpeg4", outputcrop1};
String[] complexCommand = {"-y", "-i", yourRealPath, "-filter_complex", "[0:v]setpts=0.5*PTS[v];[0:a]atempo=2.0[a]", "-map", "[v]", "-map", "[a]", "-b:v", "2097k", "-r", "60", "-vcodec", "mpeg4", outputcrop1};
===> Add two mp3 files
StringBuilder sb = new StringBuilder();
sb.append("-i ");
sb.append(textSngname);
sb.append(" -i ");
sb.append(mAudioFilename);
sb.append(" -filter_complex [0:0][1:0]concat=n=2:v=0:a=1[out] -map [out] ");
sb.append(finalfile);
---> ffmpeg.execute(sb.toString().split(" "), new ExecuteBinaryResponseHandler()
===> Add three mp3 files
StringBuilder sb = new StringBuilder();
sb.append("-i ");
sb.append(firstSngname);
sb.append(" -i ");
sb.append(textSngname);
sb.append(" -i ");
sb.append(mAudioFilename);
sb.append(" -filter_complex [0:0][1:0][2:0]concat=n=3:v=0:a=1[out] -map [out] ");
sb.append(finalfile);
---> ffmpeg.execute(sb.toString().split(" "), new ExecuteBinaryResponseHandler()
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.