2011-10-12 5 views
28

Próbuję użyć sprzętowego enkodera H264 na Androidzie, aby utworzyć wideo z kamery, i użyć FFmpeg do mux w dźwięku (wszystko na samym telefonie z Androidem)Dekoduj sprzętowy kodowany sprzętowo aparat H264 za pomocą ffmpeg w czasie rzeczywistym

Do tej pory udało mi się spakować plik H264 do pakietów rtsp i odszyfrować go za pomocą VLC (ponad UDP), więc wiem, że film jest co najmniej poprawnie sformatowany. Mam jednak problem z uzyskaniem danych wideo do ffmpeg w formacie, który może zrozumieć.

Próbowałem wysyłając te same rtsp pakietów do portu 5006 na localhost (przez UDP), a następnie dostarczenie ffmpeg z plikiem sdp że Mówi, który port lokalny strumień wideo jest w najbliższych i jak do dekodowania wideo , jeśli rozumiem, że przesyłam strumieniowo prawidłowo. Jednak to nie działa i mam problem ze zdiagnozowaniem, dlaczego, jako ffmpeg po prostu siedzi tam czekając na wejście.

Ze względu na opóźnienie i skalowalność nie mogę po prostu wysłać wideo i audio na serwer i mux tam, trzeba to zrobić na telefonie, w tak lekki sposób, jak to możliwe.

Sądzę, że szukam sugestii, jak można to osiągnąć. Optymalnym rozwiązaniem byłoby przesłanie skompresowanego pliku wideo H264 do ffmpeg przez potok, ale nie mogę wysłać parametrów pliku, którego potrzebuje do dekodowania wideo.

Mogę podać więcej informacji na żądanie, na przykład w jaki sposób skompilowano ffmpeg dla Androida, ale wątpię, czy jest to konieczne.

Aha, i sposób, w jaki uruchamiam ffmpeg jest przez linię poleceń, wolałbym raczej unikać grzebania z jni, jeśli to w ogóle możliwe.

Pomoc będzie bardzo ceniona, dzięki.

+3

dlaczego dekodujesz za pomocą ffmpeg? użyj wbudowanego obiektu MediaPlayer –

+0

Czy próbowałeś używać live555 do streamowania danych wyjściowych ffmpeg za pośrednictwem RTSP? Ponadto, czy ffmpeg nie powinien sprawdzać strumienia i samemu wyszukiwać strumienia informacji? – Shark

+0

Myślę, że Aviad ma tę prawdę. Jak wiesz, jaki format wideo produkuje kamera? –

Odpowiedz

1

Czy próbowałeś używać 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(); 

Następnie piszesz na standardowe wyjście i czytasz ze stdin i stderr. To nie jest fajka, ale powinna być lepsza niż przy użyciu interfejsu sieciowego.

+0

OP tutaj, już nie pracuję nad tym problemem (poddałem się), ale powinienem dodać, że próbowałem dokładnie to. Nie pamiętam wszystkich szczegółów, ale próbowałem używać zarówno InputStream, jak i wielu rur FIFO z systemem operacyjnym (jedna rura dla wideo, a jedna dla audio). Jednak problem z tą metodą polegał na tym, że nie byłem w stanie dostarczyć ffmpeg wystarczającej ilości informacji do zrozumienia i odkodowania pakietów wideo generowanych przez kanał kamery. Prawdziwym powodem, dla którego chciałem użyć rtsp, było to, że - teoretycznie - zapewniłby FFmpeg wystarczającą ilość informacji do odkodowania strumienia na żywo. – joebobfrank

1

Trochę za późno, ale myślę, że jest to dobre pytanie i nie ma jeszcze dobrej odpowiedzi.

Jeśli chcesz przesłać strumieniowo kamerę i mikrofon z urządzenia z Androidem, masz dwie główne opcje: implementacje Java lub NDK.

  1. Implementacja Java.

    Zamierzam tylko wspomnieć o tym pomyśle, ale w zasadzie jest to implementacja serwera RTSP i protokołu RTP w java w oparciu o te standardy: Real-Time Streaming Protocol Version 2.0 i RTP Payload Format for H.264 Video. To zadanie będzie bardzo długie i trudne. Ale jeśli robisz PhP, może być fajnie mieć ładną bibliotekę Java RTSP dla Androida.

  2. Implementacja NDK.

    Jest to alternatywa dla różnych rozwiązań. Główną ideą jest użycie biblioteki mocy C lub C++ w naszej aplikacji na Androida. W tym przypadku FFmpeg.Ta biblioteka może być kompilowana dla Androida i może obsługiwać różne architektury. Problem z tym podejściem polega na tym, że aby to osiągnąć, może być konieczne zapoznanie się z systemem Android NDK, C i C++.

    Ale jest alternatywa. Możesz owinąć bibliotekę c i użyć FFmpeg. Ale jak?

    Na przykład, używając FFmpeg Android, który został skompilowany z x264, libass, fontconfig, freetype i fribidi i obsługuje różne architektury. Ale wciąż trudno jest zaprogramować, jeśli chcesz przesyłać strumieniowo w czasie rzeczywistym, musisz zająć się deskryptorami plików i strumieniami wejściowymi i wyjściowymi.

    Najlepszą alternatywą, z punktu widzenia programowania w języku Java, jest użycie JavaCV. JavaCV wykorzystuje owijarki z powszechnie używanych bibliotek widzenia komputerowego, który obejmuje: (OpenCV, FFmpeg, etc, i zapewnia klasy narzędziowe, aby ich funkcjonalność łatwiejsze do wykorzystania na platformie Java, w tym (oczywiście) Android

    JavaCV również pochodzi. z przyspieszanym sprzętowo pełnoekranowym wyświetlaniem obrazu (CanvasFrame i GLCanvasFrame), łatwymi w użyciu metodami wykonywania kodu równolegle na wielu rdzeniach (Parallel), przyjazną dla użytkownika kalibracją geometryczną i kolorową aparatów i projektorów (GeometricCalibrator, ProCamGeometricCalibrator, ProCamColorCalibrator) , wykrywanie i porównywanie punktów charakterystycznych (ObjectFinder), zestaw klas, które implementują bezpośrednie wyrównanie obrazu systemów projektor-kamera (głównie GNImageAligner, ProjectiveTransformer, ProjectiveColorTransformer, ProCamTransformer i ReflectanceInitializer), pakiet analizy blobów (Blobs), a także różne funkcje w klasie JavaCV. Niektóre z tych klas mają również odpowiednika OpenCL i OpenGL, ich imiona kończące się CL lub zaczynające się GL, tj .: JavaCVCL, GLCanvasFrame itp

Ale w jaki sposób możemy korzystać z tego rozwiązania?

Tutaj mamy podstawową implementację do streamowania przy użyciu 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); 

Ta część kodu pokazuje, jak zainicjować obiekt FFmpegFrameRecorder o nazwie rejestrator. Obiekt ten będzie przechwytywał i kodował klatki uzyskane z kamery oraz próbki pobrane z mikrofonu.

Jeśli chcesz przechwycić podgląd w tej samej aplikacji dla systemu Android, musimy zaimplementować klasę CameraPreview, która klasyfikuje nieprzetworzone dane podawane z kamery, a utworzy podgląd i ramkę dla FFmpegFrameRecorder.

Pamiętaj, aby zastąpić ip_destination z ip komputera lub urządzenia, do którego chcesz wysłać strumień. Port może być 8080 jako przykład.

@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; 
} 

Metoda ta pokazuje implementację metody onCameraFrame że uzyskać Mata (zdjęcie) z aparatu i przekształcany jest jako rama i rejestrowane przez obiekt 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(); 
    } 
} 

samo z dźwiękiem audioData jest ShortBuffer obiekt, który będzie rejestrator przez FFmpegFrameRecorder.

Na komputerze lub urządzeniu docelowym można uruchomić następujące polecenie, aby pobrać strumień.

ffplay udp://ip_source:port 

ip_source jest IP smartphone, który jest strumieniowe kamery i mikrofonu strumień. Port musi być taki sam jak 8080.

Stworzyłem rozwiązanie w moim repozytorium github tutaj: UDPAVStreamer.

Powodzenia

Powiązane problemy