2011-10-12 62 views
28

Tôi đang cố sử dụng bộ mã hóa phần cứng H264 trên Android để tạo video từ máy ảnh và sử dụng FFmpeg để chuyển đổi âm thanh (tất cả trên điện thoại Android)Giải mã nguồn cấp dữ liệu máy ảnh H264 được mã hóa của android bằng cách sử dụng ffmpeg trong thời gian thực

Điều tôi đã thực hiện cho đến nay là đóng gói video H264 thành các gói rtsp và giải mã bằng VLC (trên UDP), vì vậy tôi biết video được định dạng đúng ít nhất. Tuy nhiên, tôi gặp sự cố khi tải dữ liệu video đến ffmpeg ở định dạng mà nó có thể hiểu được.

Tôi đã cố gắng gửi cùng rtsp gói vào một cổng 5006 trên localhost (trên UDP), sau đó cung cấp ffmpeg với file sdp mà nói với nó mà địa phương cảng dòng video được dâng lên và làm thế nào để giải mã video , nếu tôi hiểu chính xác, hãy phát trực tuyến rtsp. Tuy nhiên, điều này không có tác dụng và tôi đang gặp sự cố khi chẩn đoán lý do tại sao, như ffmpeg chỉ cần ngồi ở đó chờ đầu vào.

Vì lý do độ trễ và khả năng mở rộng, tôi không thể gửi video và âm thanh đến máy chủ và chuyển nó ở đó, nó phải được thực hiện trên điện thoại, nhẹ nhất có thể.

Điều tôi đoán tôi đang tìm kiếm là các đề xuất về cách thực hiện điều này. Giải pháp tối ưu sẽ gửi video được đóng gói H264 tới ffmpeg qua đường ống, nhưng sau đó tôi không thể gửi các thông số tệp cần để giải mã video.

Tôi có thể cung cấp thêm thông tin theo yêu cầu, như cách ffmpeg được biên soạn cho Android nhưng tôi nghi ngờ điều đó là cần thiết.

Ồ, và cách tôi bắt đầu ffmpeg là thông qua dòng lệnh, tôi thực sự muốn tránh mucking về với jni nếu đó là tất cả có thể.

Và trợ giúp sẽ được đánh giá cao, cảm ơn.

+3

tại sao bạn giải mã bằng ffmpeg? sử dụng đối tượng MediaPlayer được xây dựng trong –

+0

Bạn đã thử sử dụng live555 để truyền đầu ra của ffmpeg qua RTSP chưa? Ngoài ra, không nên ffmpeg thăm dò luồng và tự tìm hiểu thông tin luồng? – Shark

+0

Tôi nghĩ Aviad có sự thật về nó. Làm thế nào để bạn thậm chí biết những gì định dạng video máy ảnh sản xuất? –

Trả lời

1

Bạn đã thử sử dụng java.lang.Runtime chưa?

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

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

Sau đó, bạn viết tới stdout và đọc từ stdin và stderr. Nó không phải là một đường ống nhưng nó nên được tốt hơn so với sử dụng một giao diện mạng.

+0

OP ở đây, tôi không còn làm việc về vấn đề này (tôi đã từ bỏ), nhưng tôi nên thêm rằng tôi đã cố gắng chính xác điều này. Tôi không nhớ tất cả các chi tiết, nhưng tôi đã thử sử dụng cả InputStream và nhiều ống FIFO bằng cách sử dụng Hệ điều hành (Một ống cho video, một cho âm thanh). Tuy nhiên, vấn đề với phương pháp này là tôi không thể cung cấp đủ thông tin để hiểu và giải mã các gói video do nguồn cấp dữ liệu máy ảnh tạo ra. Lý do thực sự tôi muốn sử dụng rtsp là nó - về mặt lý thuyết - sẽ cung cấp đủ thông tin để giải mã luồng trực tiếp. – joebobfrank

1

Một chút trễ nhưng tôi nghĩ đây là câu hỏi hay và chưa có câu trả lời hay.

Nếu bạn muốn truyền trực tuyến máy ảnh và micrô từ thiết bị Android, bạn có hai lựa chọn chính: triển khai Java hoặc NDK.

  1. Thực hiện Java.

    Tôi chỉ đề cập đến ý tưởng nhưng về cơ bản nó thực hiện một RTSP Server và RTP Protocol trong java dựa trên các tiêu chuẩn này Real-Time Streaming Protocol Version 2.0RTP Payload Format for H.264 Video. Nhiệm vụ này sẽ rất dài và khó. Nhưng nếu bạn đang làm PhP của bạn nó có thể được tốt đẹp để có một tốt đẹp RTSP Java lib cho Android.

  2. Thực hiện NDK.

    Đây là giải pháp thay thế bao gồm các giải pháp khác nhau. Ý tưởng chính là sử dụng thư viện C hoặc C++ trong ứng dụng Android của chúng tôi. Đối với trường hợp này, FFmpeg.Thư viện này có thể được biên dịch cho Android và có thể hỗ trợ nhiều kiến ​​trúc khác nhau. Vấn đề của phương pháp này là bạn có thể cần phải tìm hiểu về NDK, C và C++ của Android để thực hiện điều này.

    Nhưng có một giải pháp thay thế. Bạn có thể bọc thư viện c và sử dụng FFmpeg. Nhưng bằng cách nào?

    Ví dụ: sử dụng FFmpeg Android, đã được biên dịch với x264, libass, fontconfig, freetype và fribidi và hỗ trợ các kiến ​​trúc khác nhau. Nhưng nó vẫn còn khó để chương trình nếu bạn muốn dòng trong thời gian thực, bạn cần phải đối phó với các mô tả tập tin và trong/ra suối.

    Cách thay thế tốt nhất, từ quan điểm lập trình Java, là sử dụng JavaCV. JavaCV sử dụng các trình bao bọc từ các thư viện máy tính thường được sử dụng bao gồm: (OpenCV, FFmpeg, v.v.) và cung cấp các lớp tiện ích để làm cho chức năng của chúng dễ sử dụng hơn trên nền tảng Java, bao gồm cả (tất nhiên) Android. với màn hình hiển thị hình ảnh toàn màn hình tăng tốc phần cứng (CanvasFrameGLCanvasFrame), các phương pháp dễ sử dụng để thực thi mã song song trên nhiều lõi (Parallel), hiệu chuẩn hình học và màu sắc thân thiện với người dùng của máy ảnh và máy chiếu (GeometricCalibrator, ProCamGeometricCalibrator, ProCamColorCalibrator) , phát hiện và kết hợp các điểm tính năng (ObjectFinder), một tập hợp các lớp thực hiện căn chỉnh trực tiếp các hệ thống máy ảnh-máy chiếu (chủ yếu là GNImageAligner, ProjectiveTransformer, ProjectiveColorTransformer, ProCamTransformerReflectanceInitializer), gói phân tích blob (Blobs), cũng như chức năng linh tinh trong lớp JavaCV. Một số các lớp học cũng có một OpenCL và OpenGL đối tác, tên của họ kết thúc với CL hoặc bắt đầu với GL, tức là .: JavaCVCL, GLCanvasFrame vv

Nhưng làm thế nào chúng ta có thể sử dụng giải pháp này?

Ở đây chúng tôi có triển khai cơ bản để phát trực tuyến bằng 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); 

Phần này của mã cho biết cách khởi tạo đối tượng FFmpegFrameRecorder được gọi là trình ghi. Đối tượng này sẽ chụp và mã hóa các khung lấy từ máy ảnh và các mẫu thu được từ micrô.

Nếu bạn muốn chụp bản xem trước trong cùng một ứng dụng Android thì chúng tôi cần triển khai Lớp CameraPreview lớp này sẽ chuyển đổi dữ liệu thô được phân phối từ Camera và nó sẽ tạo Xem trước và Khung cho FFmpegFrameRecorder.

Hãy nhớ thay thế ip_destination bằng ip của máy tính hoặc thiết bị mà bạn muốn gửi luồng. Cổng có thể là 8080 làm ví dụ.

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

Phương pháp này cho thấy việc thực hiện onCameraFrame phương pháp mà có được Mat (hình ảnh) từ camera và nó được chuyển đổi như một khung và ghi lại bởi các đối tượng 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(); 
    } 
} 

Cùng với âm thanh các audioData là một đối tượng ShortBuffer sẽ được ghi bởi các FFmpegFrameRecorder.

Trong PC hoặc thiết bị đích, bạn có thể chạy lệnh sau để nhận luồng.

ffplay udp://ip_source:port 

Điện thoại thông minh đang phát trực tuyến máy ảnh và micrô. Cổng phải giống 8080.

Tôi đã tạo một giải pháp trong kho lưu trữ github của tôi tại đây: UDPAVStreamer.

Chúc may mắn

Các vấn đề liên quan