FFmpeg trên Android


207

Tôi đã biên dịch FFmpeg (libffmpeg.so) trên Android. Bây giờ tôi phải xây dựng một ứng dụng như RockPlayer hoặc sử dụng khung đa phương tiện Android hiện có để gọi FFmpeg.

  1. Bạn có các bước / quy trình / mã / ví dụ về việc tích hợp FFmpeg trên Android / StageFright không?

  2. Bạn có thể vui lòng hướng dẫn cho tôi cách tôi có thể sử dụng thư viện này để phát lại đa phương tiện không?

  3. Tôi có một yêu cầu khi tôi đã có các luồng truyền tải âm thanh và video, mà tôi cần phải cung cấp cho FFmpeg và giải mã nó / kết xuất. Làm cách nào tôi có thể làm điều này trên Android, vì API IOMX dựa trên OMX và không thể bổ trợ FFmpeg ở đây?

  4. Ngoài ra, tôi không thể tìm thấy tài liệu về API FFmpeg cần được sử dụng để phát lại.


6
Điều này thật thú vị, tôi cũng tò mò
Axarydax

5
Làm thế nào bạn biên dịch ffmpeg để có được các tập tin .so? bạn có thể vui lòng chia sẻ các bước bạn làm theo Tôi đang làm việc trên windows với cygwin-1.7.9 và ndk r5. Làm ơn giúp tôi.
Swathi EP

Đây là một FFmpeg tương đối mới cho Android: sourceforge.net/projects/ffmpeg4android
slhck

@slhck tôi đã tải xuống mã ffmpeg từ liên kết trên và đã cố gắng biên dịch nó nhưng tôi không thể lấy được các tệp .so. nó cho thấy rất nhiều vấn đề ..
RAJESH

vui lòng giúp tôi với: stackoverflow.com/questions/14157030/ , tôi không biết nên đưa chức năng này vào đâu và chạy! .....
TharakaNirmana

Câu trả lời:


109

Dưới đây là các bước tôi đã trải qua để ffmpeg hoạt động trên Android:

  1. Xây dựng thư viện tĩnh của ffmpeg cho Android. Điều này đã đạt được bằng cách xây dựng cổng android ffmpeg ( libffmpeg ) của olvaffe bằng Hệ thống Xây dựng Android . Đơn giản chỉ cần đặt các nguồn dưới / bên ngoài và makeđi. Bạn cũng cần trích xuất bionic (libc) và zlib (libz) từ bản dựng Android, vì các thư viện ffmpeg phụ thuộc vào chúng.
  2. Tạo một thư viện động bao bọc chức năng ffmpeg bằng NDK của Android . Có rất nhiều tài liệu về cách làm việc với NDK. Về cơ bản, bạn sẽ cần phải viết một số mã C / C ++ để xuất chức năng bạn cần ra khỏi ffmpeg vào thư viện mà java có thể tương tác thông qua JNI. NDK cho phép bạn dễ dàng liên kết với các thư viện tĩnh mà bạn đã tạo ở bước 1, chỉ cần thêm một dòng tương tự như thế này vào Android.mk:LOCAL_STATIC_LIBRARIES := libavcodec libavformat libavutil libc libz

  3. Sử dụng thư viện động gói ffmpeg từ các nguồn java của bạn. Có đủ tài liệu về JNI ngoài kia, bạn sẽ ổn thôi.

Về việc sử dụng ffmpeg để phát lại, có rất nhiều ví dụ (bản thân nhị phân ffmpeg là một ví dụ hay), ở đây là một hướng dẫn cơ bản. Các tài liệu tốt nhất có thể được tìm thấy trong các tiêu đề.

Chúc may mắn :)


7
Có khá nhiều liên kết đến câu trả lời này để xây dựng ffmpeg cho Android. Đây vẫn là giải pháp tốt nhất? Liên kết Hệ thống Xây dựng Android bị hỏng - đó là gì? Có một loạt các bộ công cụ để hỗ trợ xây dựng với NDK. Tuy nhiên, tất cả đều thất bại với các lỗi xây dựng khác nhau đối với tôi và có vẻ hơi cũ. Có bất kỳ lý do tại sao một người nào đó không thể chỉ đăng một lib ffmpeg tĩnh được xây dựng?
Rob Lộ Đức

7
Để trả lời câu hỏi của riêng tôi, tôi thấy repo này là hữu ích nhất để xây dựng các trình bao bọc ffmpeg và JNI - github.com/andynicholson/android-ffmpeg-x264
Rob Lourens

67

Vì nhiều lý do, Đa phương tiện đã và không bao giờ dễ dàng trong việc đạt được nhiệm vụ mà không ảnh hưởng đến hiệu quả. ffmpeg là một nỗ lực để cải thiện nó từng ngày. Nó hỗ trợ các định dạng khác nhau của codec và container.

Bây giờ để trả lời câu hỏi làm thế nào để sử dụng thư viện này , tôi sẽ nói rằng nó không đơn giản để viết nó ở đây. Nhưng tôi có thể hướng dẫn bạn theo những cách sau .

1) Trong thư mục ffmpeg của mã nguồn, bạn có output_example.c hoặc api_example.c . Tại đây, bạn có thể thấy mã nơi mã hóa / giải mã được thực hiện. Bạn sẽ có một ý tưởng về việc API nào bên trong ffmpeg bạn nên gọi. Đây sẽ là bước đầu tiên của bạn.

2) Người chơi cá heo là một dự án nguồn mở cho Android. Hiện tại nó đang có lỗi nhưng các nhà phát triển đang làm việc liên tục. Trong dự án đó, bạn có toàn bộ thiết lập sẵn sàng mà bạn có thể sử dụng để tiếp tục điều tra. Đây là liên kết đến dự án từ code.google.com hoặc chạy lệnh " git clone https://code.google.com.vn/p/dolphin-player/ " trong một thiết bị đầu cuối. Bạn có thể thấy hai dự án có tên P và P86. Bạn có thể sử dụng một trong hai.

Mẹo bổ sung tôi muốn cung cấp là khi bạn đang xây dựng mã ffmpeg, bên trong build.sh bạn cần kích hoạt muxers / demuxers / encoder / decoder của các định dạng bạn muốn sử dụng. Khác mã tương ứng sẽ không được bao gồm trong các thư viện. Phải mất rất nhiều thời gian để tôi nhận ra điều này. Vì vậy, nghĩ về việc chia sẻ nó với bạn.

Vài điều cơ bản: Khi chúng ta nói một tệp video, ví dụ: avi, nó là sự kết hợp của cả âm thanh và video

Tệp video = Video + Âm thanh


Video = Codec + Muxer + Demuxer

codec = bộ mã hóa + bộ giải mã

=> Video = bộ mã hóa + bộ giải mã + Muxer + Demuxer (Mpeg4 + Mpeg4 + avi + avi - Ví dụ cho bộ chứa avi)


Âm thanh = Codec + Muxer + Demuxer

codec = bộ mã hóa + bộ giải mã

=> Audio = bộ mã hóa + bộ giải mã + Muxer + Demuxer (mp2 + mp2 + avi + avi - Ví dụ cho bộ chứa avi)


Codec (tên là deriverd từ sự kết hợp của en * co * der / * dec * oder) chỉ là một phần của định dạng xác định các thuật toán được sử dụng để mã hóa / giải mã khung. AVI không phải là codec, nó là một container sử dụng codec Video của Mpeg4 và codec âm thanh của mp2.

Muxer / demuxer được sử dụng để kết hợp / tách các khung khỏi một tệp được sử dụng trong khi mã hóa / giải mã.

Vì vậy, nếu bạn muốn sử dụng định dạng avi, bạn cần bật các thành phần Video + Thành phần âm thanh.

Ví dụ, đối với avi, bạn cần kích hoạt các mục sau. Bộ mã hóa mpeg4, bộ giải mã mpeg4, bộ mã hóa mp2, bộ giải mã mp2, bộ giải mã avi, bộ giải mã avi.

phewwwwwww ...

Lập trình build.sh nên chứa mã sau đây:

--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)

Hy vọng tôi không làm bạn bối rối hơn sau tất cả ...

Cảm ơn, bất kỳ trợ giúp cần thiết, xin vui lòng cho tôi biết.


1
Này, tôi muốn cảm ơn bạn rất nhiều vì thông tin đó, bạn thực sự đã giúp tôi rất nhiều, bạn có thể giúp tôi nếu tôi cần một số sau này không? Cảm ơn bạn!
idish

Tôi có thể thêm bạn vui lòng qua skype / MSN hoặc bất kỳ nền tảng trò chuyện nào khác không? Tôi có một vài câu hỏi về nó, cảm ơn bạn.
idish

2
Chắc chắn rồi..!! Nhưng sự hiện diện trực tuyến của tôi hơi thấp .. Trừ khi rất nhiều yêu cầu tôi không đăng nhập vào skype. Bạn có thể gửi thư cho tôi cho bất kỳ điều quan trọng. Email: mantykuma @ gmail
mk ..

13

Việc triển khai dễ xây dựng nhất, dễ sử dụng nhất mà tôi đã tìm thấy được thực hiện bởi nhóm theguardianproject: https://github.com/guardianproject/android-ffmpeg


Không chắc chắn, tôi đoán rằng, không có gì trong phiên bản iOS mới xuất hiện mà có thể phá vỡ điều này. Khi tôi đăng bài này, tôi vẫn có 10.7 hoặc 10.6
Guy

Bạn có biết không, làm cách nào tôi có thể chuyển đổi MSN thành âm thanh, sử dụng triển khai JNI
Mr.G

11

Tôi đã thực hiện một dự án nhỏ để định cấu hình và xây dựng X264 và FFMPEG bằng NDK của Android. Điều chính còn thiếu là một giao diện JNI phong nha để làm cho nó có thể truy cập qua Java, nhưng đó là phần dễ dàng (tương đối). Khi tôi làm tròn để làm cho giao diện JNI tốt cho mục đích sử dụng của riêng tôi, tôi sẽ đẩy nó vào.

Lợi ích đối với hệ thống xây dựng của olvaffe là nó không yêu cầu các tệp Android.mk để xây dựng các thư viện, nó chỉ sử dụng các tệp tạo tệp thông thường và chuỗi công cụ. Điều này làm cho nó ít có khả năng ngừng hoạt động hơn khi bạn kéo thay đổi mới từ FFMPEG hoặc X264.

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


Nick, dự án của bạn không được biên dịch trên OS X 10.7 libx264.a (common.o): Trong chức năng x264_param_parse': common.c:(.text+0x2864): undefined reference to _DefaultRuneLocale 'coll2: ld trả về 1 trạng thái thoát tạo: *** [x264] Lỗi 1
Yuriy Solovyov


6

Để tạo ứng dụng FFMPEG của tôi, tôi đã sử dụng dự án này ( https://github.com/hiteshsondhi88/ffmpeg-android-java ) vì vậy, tôi không phải biên dịch bất cứ điều gì. Tôi nghĩ đó là cách dễ dàng để sử dụng FFMPEG trong các ứng dụng Android của chúng tôi.

Thông tin thêm về http://hiteshsondhi88.github.io/ffmpeg-android-java/


3
Bao bọc này là rất rất rất rất rất rất chậm. 200 hình ảnh cho video mất 50-60 giây. . . nhưng thông thường ffmpeg xử lý tác vụ đó trong 4-5 giây.
Asen Sench

Dự án này không hoạt động nữa. Bạn có một số tài nguyên khác?
Ajeet

@ArsenSench bạn có giải pháp nào khác không?
Akash Dubey

3

Lấy cảm hứng từ nhiều FFmpeg khác trên các triển khai Android ngoài kia (chủ yếu là guadianproject ), tôi đã tìm thấy một giải pháp (cũng có hỗ trợ Lame).

(khập khiễng và FFmpeg: https://github.com/intervigilium/liblamehttp://bambuser.com/opensource )

để gọi 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();

và để xử lý đầu ra giao diện điều khiển:

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.
        }
    }
}

Kinh nghiệm của bạn với Guardianproject là gì?
XY

3

Điều kỳ lạ là dự án này chưa được đề cập: AndroidFFmpeg từ Appunite

Nó có các hướng dẫn từng bước khá chi tiết để sao chép / dán vào dòng lệnh, cho những người lười biếng như tôi))


3

Tôi đã có cùng một vấn đề, tôi tìm thấy hầu hết các câu trả lời ở đây ngày. Cuối cùng tôi đã viết một trình bao bọc trên FFMPEG để truy cập từ Android với một dòng mã.

https://github.com/madhavanmalolan/ffmpegandroidl Library


1
Có vẻ như bạn đã biên dịch FFmpeg v2.8.4, có kế hoạch nào để nâng cấp FFmpeg không? Chúng tôi đang tìm kiếm giải pháp Android có phiên bản FFmpeg mới nhất (có thể là 3.2 hoặc 3.4).
sappu

Đúng. Tôi có ý định di chuyển nó đến 3.x github.com/madhavanmalolan/ffmpegandroidlibrary/milestone/1 Bạn có thể cố gắng sửa đổi xây dựng kịch bản ở đây và biên dịch cho 3,4 github.com/madhavanmalolan/ffmpegandroidlibrary/wiki/...
Madhavan Malolan

Cảm ơn @Madhvan. Tôi đang xây dựng thư viện ffmpeg trên windows. Chỉ cần tự hỏi tất cả những gì cần phải thay đổi trong github.com/madhavanmalolan/ffmpegandroidl Library / wiki / Lỗi để xây dựng?
sappu

0

Đầu tiên, thêm sự phụ thuộc của thư viện FFmpeg

implementation 'com.writingminds:FFmpegAndroid:0.3.2'

Sau đó tải trong hoạt động

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();

}

Cũng sử dụng một tính năng khác của 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()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.