Android 基於FFMpeg命令的轉流器

目前市場上的流行的攝像頭都是視頻流都是rtsp協議的,而目前的流媒體的服務器推流都是使用rmtp協議,這是需要把一個rtsp的視頻流轉成rtmp的視頻流,牛人可以自己寫一個拉流器和推流器,自己來實現,對於剛涉及到這個領域的人來說可以考慮使用目前很流行,且很牛B的開源框架來實現,它就是大名鼎鼎的FFMpeg,至於它如何的牛,百度一下,目前播放器裏都有它的身影,比如要實現把rtsp轉rtmp只需要一個行命令就行“ffmpeg -re -i rtsp://192.168.191.3/mpeg4cif -vcodec copy -acodec copy -f flv -y rtmp://www.ossrs.net/live/helloworld”,這個命令的意思就是把視頻流從192.168.191.3拉下來,然後視頻編碼與音頻編碼複用,打包成flv格式的文件流推送到www.ossrs.net的流媒體服務器上去。

但這個框架是使用C語言編寫的,android上如何遷移呢,對於新手來說,把這個C框架遷移到android平臺也是個麻煩,但這個問題已經被解決了,目前只需要加一行包依賴就可以讓你的應用使用上FFmpeg,目前已經有一些開源項目做這個事情,http://writingminds.github.io/ffmpeg-android-java/

先在你的android 加上一行依賴

  •  Grab via gradle
    compile 'com.writingminds:FFmpegAndroid:0.3.2'
  • Or Maven
    <dependency>
      <groupId>com.writingminds</groupId>
      <artifactId>FFmpegAndroid</artifactId>
      <version>0.3.2</version>
    </dependency>


下面的代碼就是基於ffmpeg的命令的轉流器實現

emptypackage com.foresee.wificamera.service.impl;

import android.content.Context;
import android.util.Log;

import com.foresee.wificamera.service.LivePusher;
import com.github.hiteshsondhi88.libffmpeg.ExecuteBinaryResponseHandler;
import com.github.hiteshsondhi88.libffmpeg.FFmpeg;
import com.github.hiteshsondhi88.libffmpeg.LoadBinaryResponseHandler;
import com.github.hiteshsondhi88.libffmpeg.exceptions.FFmpegCommandAlreadyRunningException;
import com.github.hiteshsondhi88.libffmpeg.exceptions.FFmpegNotSupportedException;

import java.util.regex.Pattern;

/**
 * 使用FFMPEG進行推流的推流器
 * Created by Administrator on 2017-03-14.
 */

public class FFmpegLivePusher implements LivePusher {

    private static final String LOG_TAG = "FFmpegLivePusher";
    private static final String FFMPEG_PUSH_COMMAND_PATTERN = "-re -i %s -vcodec copy -acodec copy -f flv -y %s";
    private String inputURI;
    private String outputURI;

    private Context context;
    private FFmpeg ffmpeg;
    private boolean stopFlag = false;
    private int rescueNum = 0;
    private boolean pushErrorFlag = false;
    private final Pattern pattern = Pattern.compile("frame=(\\s*\\w*\\s)fps=(\\s*\\w*\\s*)");

    public FFmpegLivePusher(Context _context, String _inputURI, String _outputURI) throws FFmpegNotSupportedException {
        this.inputURI = _inputURI;
        this.outputURI = _outputURI;
        this.context = _context;

        ffmpeg = FFmpeg.getInstance(context);

        ffmpeg.loadBinary(new LoadBinaryResponseHandler(){
            @Override
            public void onFailure() {
                Log.e(LOG_TAG, "加載FFmpeg出錯!");
                ffmpeg = null;
            }

            @Override
            public void onFinish() {
                Log.i(LOG_TAG, "load FFmpeg success!");

            }
        });

    }

    @Override
    public void startPush() {
        if(ffmpeg != null){

            String[] commands = String.format(FFMPEG_PUSH_COMMAND_PATTERN,
                    inputURI, outputURI).split(" ");

            if(ffmpeg.isFFmpegCommandRunning()){
                ffmpeg.killRunningProcesses();
            }

            try {
                ffmpeg.execute(commands, new ExecuteBinaryResponseHandler(){

                    @Override
                    public void onProgress(String message) {
                        Log.i(LOG_TAG, message);
                        //根據輸出特定的日誌來判斷是否在推流
                        if(pattern.matcher(message).find()){
                            //說明正在推流
                            pushErrorFlag = false;
                            rescueNum = 0;
                        }
                    }

                    @Override
                    public void onFailure(String message) {
                        Log.i(LOG_TAG, message);
                        pushErrorFlag = true;
                    }

                    @Override
                    public void onFinish() {
                        if(!stopFlag){
                            if (rescueNum < 10){
                                Log.e(LOG_TAG, "推流過程異常停止,重新啓動推流...");
                                startPush();
                                rescueNum++;
                                pushErrorFlag = true;
                            }else{
                                Log.e(LOG_TAG, "推流異常停止,經搶救10次無效");
                                rescueNum = 0;
                                pushErrorFlag = true;
                            }

                        }
                    }
                });
            } catch (FFmpegCommandAlreadyRunningException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void stopPush() {
        stopFlag = true;
        if(ffmpeg.isFFmpegCommandRunning()){
            ffmpeg.killRunningProcesses();
        }
    }

    public boolean isPushError(){
        return pushErrorFlag;
    }

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章