目前市場上的流行的攝像頭都是視頻流都是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>
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;
}
}