2011-10-12 122 views
28

我試圖使用Android上的硬件H264編碼器創建相機視頻,並使用FFmpeg在音頻中複合(所有這些都在Android手機本身上)使用ffmpeg實時解碼android的硬件編碼的H264相機饋送

我到目前爲止所完成的工作是將H264視頻打包成rtsp數據包,並使用VLC(超過UDP)進行解碼,因此我知道視頻至少格式正確。但是,我無法以其能夠理解的格式將視頻數據提供給ffmpeg

我試過在本地主機上發送相同rtsp數據包發送到端口5006(基於UDP),然後提供ffmpegsdp文件,告訴它哪個本地端口的視頻流上進來,以及如何將視頻解碼,如果我理解rtsp正確流式傳輸。然而,這不起作用,我在診斷原因時遇到了問題,因爲ffmpeg就在那裏等待輸入。

由於延遲和可擴展性的原因,我不能只是將視頻和音頻發送到服務器並將其複用到那裏,它必須在手機上以儘可能輕的方式完成。

我想我正在尋找的是如何實現這一點的建議。最佳解決方案是通過管道將分組化的H264視頻發送到ffmpeg,但後來我無法發送ffmpegsdp文件參數來解碼視頻。

我可以根據請求提供更多信息,例如ffmpeg是如何編譯爲Android的,但我懷疑這是必要的。

哦,而我開始ffmpeg的方式是通過命令行,我真的寧願避免與jni混淆,如果這是可能的話。

和幫助將不勝感激,謝謝。

+3

你爲什麼使用ffmpeg解碼?使用內置的MediaPlayer對象 –

+0

您是否嘗試過使用live555通過RTSP傳輸ffmpeg的輸出?另外,不應該ffmpeg探測流,並找出流信息本身? – Shark

+0

我認爲Aviad有它的真相。你甚至怎麼知道相機產生的視頻格式? –

回答

1

您是否嘗試過使用java.lang.Runtime?

String[] parameters = {"ffmpeg", "other", "args"}; 
Program program Runtime.getRuntime().exec(parameters); 

InputStream in = program.getInputStream(); 
OutputStream out = program.getOutputStream(); 
InputStream err = program.getErrorStream(); 

然後你寫入標準輸出並從stdin和stderr讀取。這不是一個管道,但它應該比使用網絡接口更好。

+0

OP在這裏,我不再在這個問題上工作(我放棄了),但我應該補充一點,我確實嘗試了這個。我不記得所有的細節,但我嘗試使用InputStream和使用操作系統的多個FIFO管道(一個視頻管道,一個音頻管道)。然而,這種方法的問題在於我無法提供足夠的信息來理解和解碼攝像頭饋送生成的視頻數據包。我想使用rtsp的真正原因是,它理論上會爲FFmpeg提供足夠的信息來解碼實時流。 – joebobfrank

1

有點晚了,但我認爲這是一個很好的問題,它還沒有一個好的答案。

如果你想從Android設備流式傳輸相機和麥克風,你有兩個主要的選擇:Java或NDK實現。

  1. Java實現。

    我只想提到這個想法,但基本上它是基於這些標準Real-Time Streaming Protocol Version 2.0RTP Payload Format for H.264 Video在java中實現RTSP服務器和RTP協議。這項任務將會非常艱鉅。但是如果你正在做PhP,那麼爲Android設置一個不錯的RTSP Java lib可能會很好。

  2. NDK實施。

    這是替代包括各種解決方案。主要思想是在Android應用程序中使用強大的C或C++庫。對於這個例子,FFmpeg。該庫可以針對Android進行編譯,並可以支持各種體系結構。 這種方法的問題是,您可能需要了解Android NDK,C和C++來完成此操作。

    但有一個選擇。你可以包裝c庫並使用FFmpeg。但是如何?

    例如,使用FFmpeg Android,它使用x264,libass,fontconfig,freetype和fribidi編譯並支持各種體系結構。但是,如果您想實時流式傳輸,則需要處理文件描述符和輸入/輸出流,但仍難以編程。

    從Java編程的角度來看,最好的選擇是使用JavaCV。 JavaCV使用來自包括計算機視覺的常用包裝的庫:(OpenCVFFmpeg等,並提供實用工具類,使其功能更容易在Java平臺上使用,包括(安卓當然)

    JavaCV也來了。硬件加速的全屏圖像顯示(CanvasFrameGLCanvasFrame),易於使用的方法在多個內核上並行執行代碼(Parallel),用戶友好的相機和投影儀的幾何和顏色校準(GeometricCalibrator,) ,特徵點的檢測和匹配(ObjectFinder),一組實現投影機 - 照相機系統的直接圖像對齊的類(主要爲GNImageAligner,ProjectiveTransformer,ProjectiveColorTransformer,ProCamTransformerReflectanceInitializer),斑點分析包(Blobs)以及JavaCV類中的其他功能。有些類也有OpenCL和OpenGL的對手,他們的名字與CL結束或開始GL,即:JavaCVCLGLCanvasFrame

但如何才能使用此解決方案?

這裏我們有一個使用UDP進行流式傳輸的基本實現。

String streamURL = "udp://ip_destination:port"; 
recorder = new FFmpegFrameRecorder(streamURL, frameWidth, frameHeight, 1); 
recorder.setInterleaved(false); 
// video options // 
recorder.setFormat("mpegts"); 
recorder.setVideoOption("tune", "zerolatency"); 
recorder.setVideoOption("preset", "ultrafast"); 
recorder.setVideoBitrate(5 * 1024 * 1024); 
recorder.setFrameRate(30); 
recorder.setSampleRate(AUDIO_SAMPLE_RATE); 
recorder.setVideoCodec(AV_CODEC_ID_H264); 
recorder.setAudioCodec(AV_CODEC_ID_AAC); 

這部分代碼顯示瞭如何初始化稱爲記錄器的FFmpegFrameRecorder對象。該對象將捕獲並編碼從相機獲取的幀和從麥克風獲得的樣本。

如果你想在同一個Android應用程序中捕捉預覽,那麼我們需要實現一個CameraPreview類,這個類將轉換從相機提供的原始數據,它將爲FFmpegFrameRecorder創建預覽和框架。

請記住用您想要發送流的pc或設備的IP替換ip_destination。例如,端口可以是8080。

@Override 
public Mat onCameraFrame(Mat mat) 
{ 
    if (audioRecordRunnable == null) { 
     startTime = System.currentTimeMillis(); 
     return mat; 
    } 
    if (recording && mat != null) { 
     synchronized (semaphore) { 
      try { 
       Frame frame = converterToMat.convert(mat); 
       long t = 1000 * (System.currentTimeMillis() - startTime); 
       if (t > recorder.getTimestamp()) { 
        recorder.setTimestamp(t); 
       } 
       recorder.record(frame); 
      } catch (FFmpegFrameRecorder.Exception e) { 
       LogHelper.i(TAG, e.getMessage()); 
       e.printStackTrace(); 
      } 
     } 
    } 
    return mat; 
} 

此方法顯示了onCameraFrame方法獲得來自攝像機的墊(圖片)的執行,並將其轉換爲一幀,並由FFmpegFrameRecorder對象記錄。

@Override 
public void onSampleReady(ShortBuffer audioData) 
{ 
    if (recorder == null) return; 
    if (recording && audioData == null) return; 

    try { 
     long t = 1000 * (System.currentTimeMillis() - startTime); 
     if (t > recorder.getTimestamp()) { 
      recorder.setTimestamp(t); 
     } 
     LogHelper.e(TAG, "audioData: " + audioData); 
     recorder.recordSamples(audioData); 
    } catch (FFmpegFrameRecorder.Exception e) { 
     LogHelper.v(TAG, e.getMessage()); 
     e.printStackTrace(); 
    } 
} 

同與所述音頻的audioDataShortBuffer對象,這將是由FFmpegFrameRecorder記錄器。

在PC或設備目標中,您可以運行以下命令來獲取流。

ffplay udp://ip_source:port 

ip_source是流式傳輸相機和麥克風流的智能手機的IP地址。端口必須是相同的8080.

我在我的github資源庫中創建了一個解決方案:UDPAVStreamer

祝你好運