FFmpeg на Android


207

У мене складено FFmpeg (libffmpeg.so) на Android. Тепер мені потрібно створити або такий додаток, як RockPlayer, або використовувати існуючу мультимедійну рамку Android, щоб викликати FFmpeg.

  1. Чи є у вас кроки / процедури / код / ​​приклад інтеграції FFmpeg в Android / StageFright?

  2. Чи можете ви, будь ласка, вказувати мені, як я можу використовувати цю бібліотеку для відтворення мультимедіа?

  3. У мене є вимога, коли у мене вже є аудіо- та відеопотоки, які мені потрібно подати до FFmpeg та отримати його розшифровку / рендеринг. Як я можу це зробити на Android, оскільки API IOMX засновані на OMX і тут не можна підключати FFmpeg?

  4. Крім того, я не зміг знайти документацію про API FFmpeg, яку потрібно використовувати для відтворення.


7
це цікаво, мені також цікаво
Аксарідакс

5
як ви склали ffmpeg для отримання файлів .so? чи можете ви поділитися кроками, які ви виконували. Я працюю на windows з cygwin-1.7.9 та ndk r5. Будь ласка, допоможи мені.
Swathi EP

Ось відносно новий FFmpeg для Android: sourceforge.net/projects/ffmpeg4android
slhck

@slhck Я завантажив код ffmpeg із вищевказаного посилання і спробував скомпілювати його, але я не в змозі отримати .so файли. це показує багато питань ..
RAJESH

будь ласка, допоможіть мені: stackoverflow.com/questions/14157030/… , я не знаю, куди включити цю функцію та запустити! .....
TharakaNirmana

Відповіді:


109

Ось кроки, які я пройшов, щоб отримати ffmpeg для роботи на Android:

  1. Створіть статичні бібліотеки ffmpeg для Android. Це було досягнуто за рахунок будівництва FFmpeg андроїда порт olvaffe в ( libffmpeg ) з використанням Android системи збирання . Просто розмістіть джерела під / зовні та makeпоза. Вам також потрібно буде витягнути bionic (libc) та zlib (libz) з версії Android, оскільки бібліотеки ffmpeg залежать від них.
  2. Створіть динамічну бібліотеку, що завершує функцію ffmpeg, використовуючи Android NDK . Там багато документації про те, як працювати з NDK. В основному вам потрібно буде написати деякий код C / C ++, щоб експортувати потрібну вам функцію з ffmpeg в бібліотеку Java, з якою може взаємодіяти JNI. NDK дозволяє легко зв’язувати статичні бібліотеки, створені на кроці 1, просто додайте рядок, подібний до цього на Android.mk:LOCAL_STATIC_LIBRARIES := libavcodec libavformat libavutil libc libz

  3. Використовуйте динамічну бібліотеку ffmpeg-обгортки зі своїх джерел Java. Там вистачає документації на JNI, вам слід добре.

Що стосується використання ffmpeg для відтворення, є багато прикладів (сам двійковий файл ffmpeg є хорошим прикладом), ось основний підручник. Найкращу документацію можна знайти в заголовках.

Удачі :)


7
На цю відповідь є досить багато посилань для створення ffmpeg для Android. Це все-таки найкраще рішення? Посилання системи Android Build Build порушено - що це повинно бути? Існує маса наборів інструментів, які допомагають будувати NDK. Однак всі вони провалюються з різними помилками побудови для мене, і здаються трохи старими. Чи є якась причина, чому хтось не може просто розмістити вбудовану статичну ffmpeg lib?
Роб Луренс

7
Щоб відповісти на власне запитання, я вважав, що ця репо є найкориснішою для побудови обгортки ffmpeg та JNI - github.com/andynicholson/android-ffmpeg-x264
Rob Lourens

68

З різних причин мультимедіа було і ніколи не буває простим у плані досягнення поставленого завдання без шкоди для ефективності. ffmpeg - це зусилля, щоб покращувати його з кожним днем. Він підтримує різні формати кодеків і контейнерів.

Тепер, щоб відповісти на питання, як користуватися цією бібліотекою , я б сказав, що написати її тут не так просто. Але я можу направити вас наступними способами .

1) Всередині каталогу ffmpeg вихідного коду ви маєте output_example.c або api_example.c . Тут ви можете побачити код, де робиться кодування / декодування. Ви отримаєте уявлення про те, до якого API в ffmpeg слід зателефонувати. Це був би ваш перший крок.

2) Програвач Dolphin - це проект з відкритим кодом для Android. Наразі він має помилки, але розробники працюють постійно. У цьому проекті у вас є ціла програма, яку ви можете використовувати для продовження розслідування. Ось посилання на проект із code.google.com або запустіть команду " git clone https://code.google.com/p/dolphin-player/ " у терміналі. Ви можете побачити два проекти під назвою P та P86. Ви можете використовувати будь-який з них.

Додатковим підказом, який я хотів би запропонувати, є те, що, коли ви будуєте код ffmpeg, всередині build.sh потрібно ввімкнути муксери / демуксери / кодери / декодери форматів, які ви хочете використовувати. Інакше відповідний код не буде включений до бібліотек. Мені знадобилося багато часу, щоб зрозуміти це. Тож подумав поділитися цим із вами.

Мало основ: Коли ми говоримо про відеофайл, наприклад: avi, це поєднання як аудіо, так і відео

Відеофайл = Video + Audio


Відео = Codec + Muxer + Demuxer

codec = кодер + декодер

=> Відео = кодер + декодер + Муксер + Демуксер (Mpeg4 + Mpeg4 + avi + avi - Приклад для контейнера avi)


Аудіо = Codec + Muxer + Demuxer

codec = кодер + декодер

=> Audio = кодер + декодер + Muxer + Demuxer (mp2 + mp2 + avi + avi - приклад для контейнера avi)


Кодек (назва похідне від комбінації en * co * der / * dec * oder) - це лише частина формату, яка визначає алгоритми, що використовуються для кодування / декодування кадру. AVI не є кодеком, це контейнер, який використовує Video codec Mpeg4 та Audio codec з mp2.

Muxer / demuxer використовується для об'єднання / відокремлення кадрів з файлу, який використовується під час кодування / декодування.

Отже, якщо ви хочете використовувати формат avi, вам потрібно включити Відео компоненти + Аудіо компоненти.

Наприклад, для avi потрібно включити наступне. mpeg4 Encoder, декодер mpeg4, кодер mp2, декодер mp2, avi muxer, avi demuxer.

phwwwwwww ...

Програмно 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)

Сподіваюся, я ідід не збентежувати вас більше після всього цього ...

Дякую, будь-яка допомога потрібна, будь ласка, повідомте мене про це.


1
Гей, я хотів би подякувати вам за цю інформацію, ви дійсно дуже мені допомогли, чи можна вам допомогти мені, якщо мені знадобиться трохи пізніше? Дякую!
ідіш

Чи можу я додати вас будь-ласка через skype / MSN або будь-яку іншу платформу чату? У мене є кілька запитань щодо цього, дякую.
ідіш

2
Звичайно .. !! Але моя присутність в режимі он-лайн трохи низька. Якщо це дуже не потрібно, я не входжу в скайп. Ви можете надіслати мені пошту для будь-яких важливих речей. Електронна пошта: mantykuma@gmail.com
mk ..

13

Найпростіша у створенні, проста у використанні реалізація, яку я знайшов, зроблена командою theguardianproject: https://github.com/guardianproject/android-ffmpeg


Не впевнений, я здогадуюсь, що це так, ніщо в новій версії iOS не приходить в голову, що могло б це зламати. Коли я опублікував це, у мене ще було 10,7 або 10,6
Хлопець

Ви знаєте, як я можу конвертувати 3gp в аудіо, використовуючи реалізацію JNI
Mr.G

11

Я зробив невеликий проект з налаштування та створення X264 та FFMPEG за допомогою Android NDK. Головне, чого не вистачає, - це пристойний інтерфейс JNI, щоб зробити його доступним через Java, але це легка частина (відносно). Коли я перейду до того, щоб зробити інтерфейс JNI корисним для власних цілей, я підключу його.

Перевага перед системою збирання olvaffe полягає в тому, що для створення бібліотек їй не потрібні файли Android.mk, вона просто використовує звичайні файли та ланцюжок інструментів. Це робить набагато меншою ймовірність припинити роботу, коли ви перетянете нові зміни з FFMPEG або X264.

https://github.com/halfninja/android-ffmpeg-x264


Нік, ваш проект не збирається на OS X 10.7 libx264.a (common.o): у функції x264_param_parse': common.c:(.text+0x2864): undefined reference to _DefaultRuneLocale 'collection2: ld повернув 1 статус виходу make: *** [x264] Помилка 1
Юрій Соловйов

9

Після численних досліджень, саме зараз це найновіша складена бібліотека для Android, яку я знайшов:

https://github.com/bravobit/FFmpeg-Android

  • У цей момент використовується FFmpeg release n4.0-39-gda39990
  • Включає FFmpeg та FFProbe
  • Містить інтерфейс Java для запуску команд
  • FFprobe або FFmpeg можна було видалити з APK, перевірте вікі https://github.com/bravobit/FFmpeg-Android/wiki

6

Щоб зробити свою заявку на FFMPEG, я використав цей проект ( https://github.com/hiteshsondhi88/ffmpeg-android-java ), тому мені нічого не потрібно складати. Я думаю, що це простий спосіб використання FFMPEG у наших додатках для Android.

Більше інформації про http://hiteshsondhi88.github.io/ffmpeg-android-java/


3
Ця обгортка дуже-дуже-дуже-дуже повільна. 200 зображень до відео займає 50-60 секунд. . . але зазвичай ffmpeg справляється із цим завданням за 4-5 секунд.
Арсен Сенч

Цей проект більше не працює. Чи є у вас інші ресурси?
Ajeet

@ArsenSench у вас є інше рішення?
Акаш Дабей

3

Натхненний багатьма іншими FFmpeg на реалізаціях Android там (головним чином, 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.
        }
    }
}

Який у вас досвід роботи з проектом опікуна?
XY

3

Дивно, що цей проект не згадували: AndroidFFmpeg від Appunite

У ньому є досить докладні покрокові інструкції скопіювати та вставити в командний рядок для ледачих людей, як я))


3

У мене було те саме питання, більшість відповідей тут я знайшов застарілими. Я закінчив писати обгортку на FFMPEG для доступу з Android з одним рядком коду.

https://github.com/madhavanmalolan/ffmpegandroidlibrary


1
Здається, ви склали FFmpeg v2.8.4, чи плануєте оновити FFmpeg? Ми шукаємо рішення для Android з найновішою версією FFmpeg (може бути 3,2 або 3,4).
саппу

Так. Я маю намір перенести його на 3.x github.com/madhavanmalolan/ffmpegandroidlibrary/milestone/1 Ви можете спробувати змінити сценарій збірки тут і компілювати для 3.4 github.com/madhavanmalolan/ffmpegandroidlibrary/wiki/…
Madhavan Malolan

Дякую @Madhvan Я будую бібліотеку ffmpeg на windows. Просто цікаво, що потрібно змінити на github.com/madhavanmalolan/ffmpegandroidlibrary/wiki/… , щоб побудувати?
саппу

1

Спочатку додайте залежність бібліотеки 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();

}

Також використовуйте ще одну функцію від 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()
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.