2010-12-29 40 views
21

Tôi cần phát luồng âm thanh trực tiếp, thực ra đó là đài phát thanh. Vấn đề là tôi cũng cần quản lý bộ đệm 20 phút để phát trực tuyến. Theo như tôi hiểu nó không dễ thực hiện với android.Đệm luồng âm thanh

Trước hết tôi đã kiểm tra MediaPlayer, nhưng nó không cung cấp bất kỳ phương pháp nào để quản lý bộ đệm. Trong thực tế, bạn thậm chí không thể thiết lập kích thước bộ đệm trực tiếp.

Thứ hai, tôi đã cố gắng quản lý bộ đệm bằng cách sử dụng các tệp cục bộ: dần dần tải luồng xuống một tệp tạm thời và chuyển chúng. Nhưng khi bạn muốn chuyển sang tập tin sau (thay đổi nguồn dữ liệu trong MediaPlayer), âm thanh không được phát liên tục. Bạn có thể nghe thấy gián đoạn ngắn.

Ý tưởng cuối cùng là sử dụng proxy luồng. Thông thường nó được sử dụng để chơi các luồng trong các phiên bản Android dưới đây 8. Trong proxy dòng bạn tạo ServerSocket, đọc từ dòng âm thanh và ghi vào máy nghe nhạc. Vì vậy, trên thực tế tôi có thể quản lý bộ đệm ở đó. Tôi có thể lưu trữ luồng và ghi vào MediaPlayer bất cứ điều gì tôi muốn. Nhưng. Ứng dụng này không hoạt động với Android 8.

Tôi đã nhận ngoại lệ: Đặt lại kết nối theo đồng đẳng java.net.SocketException: Đặt lại kết nối theo peer. MediaPlayer 8 không muốn đọc dữ liệu từ ổ cắm.

Vì vậy, tôi có hai câu hỏi: 1) Cách nào khác để triển khai bộ đệm luồng? 2) cách điều chỉnh StreamProxy cho android 8?

Bất kỳ ý tưởng nào được đánh giá cao.

Cảm ơn

+0

"2) làm thế nào để thích ứng StreamProxy cho android 8?" ... Trừ khi bạn hiển thị mã bạn đang sử dụng, nó không thể cho bất cứ ai để giúp đỡ. – Squonk

+1

Cách bạn đang viết luồng cho MediaPlayer bạn có thể giải thích thêm một chút không? –

Trả lời

8

tôi sử dụng cùng một StreamProxy rằng chàng trai sử dụng cho dự án NPR - https://code.google.com/p/npr-android-app/source/browse/Npr/src/org/npr/android/news/StreamProxy.java

Vì vậy, nó được ban dòng âm thanh:

String url = request.getRequestLine().getUri(); 
    HttpResponse realResponse = download(url); 
    ... 
    InputStream data = realResponse.getEntity().getContent(); 

Và viết từ dòng này để client socket:

byte[] buff = new byte[1024 * 50]; 
    while (isRunning && (readBytes = data.read(buff, 0, buff.length)) != -1) { 
    client.getOutputStream().write(buff, 0, readBytes); 
    } 

(Bạn có thể lấy toàn bộ mã từ liên kết a Bove)

Và cuối cùng cách họ khởi tạo các cầu thủ (PlaybackService):.

if (stream && sdkVersion < 8) { 
    if (proxy == null) { 
     proxy = new StreamProxy(); 
     proxy.init(); 
     proxy.start(); 
    } 
    String proxyUrl = String.format("http://127.0.0.1:%d/%s", proxy.getPort(), url); 
    playUrl = proxyUrl; 
    } 
    ... 
    mediaPlayer.setDataSource(playUrl); 
    mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); 
    mediaPlayer.prepareAsync(); 

Vì vậy, họ kiểm tra phiên bản SDK. Nhưng nếu tôi bỏ qua kiểm tra này và sử dụng proxy cho SDK 8 cũng như tôi nhận được một ngoại lệ. Thật lạ khi MediaPlayer thậm chí không cố gắng đọc luồng:

12-30 15:09:41.576: DEBUG/StreamProxy(266): downloading... 
12-30 15:09:41.597: DEBUG/StreamProxy(266): reading headers 
12-30 15:09:41.597: DEBUG/StreamProxy(266): headers done 
12-30 15:09:41.647: DEBUG/StreamProxy(266): writing to client 
12-30 15:09:41.857: INFO/AwesomePlayer(34): mConnectingDataSource->connect() returned - 1007 
12-30 15:09:41.857: ERROR/MediaPlayer(266): error (1, -1007) 
12-30 15:09:41.867: ERROR/MediaPlayer(266): Error (1,-1007) 
12-30 15:09:41.867: WARN/AudioService(266): onError(1, -1007) 
12-30 15:09:41.867: WARN/AudioService(266): MediaPlayer refused to play current item. Bailing on prepare. 
12-30 15:09:41.867: WARN/AudioService(266): onComplete() 
12-30 15:09:42.097: ERROR/StreamProxy(266): Connection reset by peer 
     java.net.SocketException: Connection reset by peer 
     at org.apache.harmony.luni.platform.OSNetworkSystem.writeSocketImpl(Native Method) 
     at org.apache.harmony.luni.platform.OSNetworkSystem.write(OSNetworkSystem.java:723) 
     at org.apache.harmony.luni.net.PlainSocketImpl.write(PlainSocketImpl.java:578) 
     at org.apache.harmony.luni.net.SocketOutputStream.write(SocketOutputStream.java:59) 
     at com.skyblue.service.media.StreamProxy.processRequest(StreamProxy.java:204) 
     at com.skyblue.service.media.StreamProxy.run(StreamProxy.java:103) 
     at java.lang.Thread.run(Thread.java:1096) 

Dường như MediaPlayer trở nên thông minh hơn. Và nếu tôi vượt qua url như vậy "http://127.0.0.1:%d/%s" nó muốn nhận được không chỉ byte nhưng "đầy đủ" http trả lời.

Ngoài ra tôi tự hỏi liệu có cách nào khác thực hiện đệm không? Như tôi biết MediaPlayer chỉ tiêu thụ các tệp và url. Giải pháp với các tệp không hoạt động. Vì vậy, tôi phải sử dụng ổ cắm để truyền luồng.

Cảm ơn

+2

Hi Eugenious, Liên kết bạn đăng của dự án NPR không còn thoát ra được nữa, bạn có thể đăng liên kết hợp lệ không? –

+0

Đây là liên kết chính xác: https://code.google.com/p/npr-android-app/source/browse/Npr/src/org/npr/android/news/StreamProxy.java – stanri

+0

Hi Eugenious, bạn có tìm thấy không giải pháp cho vấn đề ban đầu? Đã sửa chữa được đăng bởi công việc brack cho bạn? – burakk

2

Mediaplayer từ SDK 8 sẽ không thể đọc url proxy. Nhưng dựa trên công việc hiện tại của tôi, điều này khác với thiết bị. Trong Samsung ACE của tôi (SDK 8) kết nối proxy hoạt động tốt, nhưng trong HTC Incredible S của tôi, kết nối proxy sẽ cung cấp cho cùng một vấn đề như bạn có.Một kết nối trực tiếp đến dòng âm thanh hoạt động tốt nhưng điều này gây ra blips và boops trong một số thiết bị như EVO trên chạy nước rút.

Bạn có nhận được giải pháp cho vấn đề này không? Làm thế nào bạn xử lý này?

-Hari

+0

Tôi đã trải nghiệm cùng một hành vi và tôi chắc chắn 99% rằng nó được gắn với trình phát phương tiện cơ bản. Trên hầu hết các thiết bị SDK 8 sử dụng trình phát đa phương tiện hiện tại (AwesomePlayer/StageFright), 'thiết lập lại kết nối' sẽ xảy ra. Một số thiết bị, tuy nhiên, sử dụng một cầu thủ phương tiện truyền thông đã lỗi thời (PVPlayer), và không có lỗi sẽ xảy ra. – brack

5

Tôi vừa kiểm tra bản sửa lỗi sau và hoạt động. Sự cố này được gây ra bởi "\ n" được sử dụng trong tiêu đề trong phương thức processRequest() của StreamProxy. Thay đổi điều này thành "\ r \ n" sẽ làm cho lỗi biến mất.

Để triển khai phát trực tuyến tùy chỉnh, tôi đã sử dụng tính năng này trong quá khứ, mặc dù nó dường như chủ yếu cho đài phát thanh vô tuyến. http://blog.pocketjourney.com/2009/12/27/android-streaming-mediaplayer-tutorial-updated-to-v1-5-cupcake/

+0

Url bài đăng trên blog không tồn tại. – Murat

3

Khi bạn tìm kiếm hoặc bỏ qua hoặc kết nối bị mất và MediaPlayer tiếp tục kết nối lại với máy chủ proxy, bạn phải gửi phản hồi này với Trạng thái 206 sau khi nhận được yêu cầu và phạm vi (int) từ máy khách.

String headers += "HTTP/1.1 206 Partial Content\r\n"; 
headers += "Content-Type: audio/mpeg\r\n"; 
headers += "Accept-Ranges: bytes\r\n"; 
headers += "Content-Length: " + (fileSize-range) + "\r\n"; 
headers += "Content-Range: bytes "+range + "-" + fileSize + "/*\r\n"; 
headers += "\r\n"; 

Và khi bạn nhận được yêu cầu từ MediaPlayer không chứa Phạm vi trong tiêu đề HTTP, sau đó nó đang yêu cầu một tập tin dòng mới, trong trường hợp này đáp ứng tiêu đề của bạn sẽ giống như thế này:

String headers = "HTTP/1.1 200 OK\r\n"; 
headers += "Content-Type: audio/mpeg\r\n"; 
headers += "Accept-Ranges: bytes\r\n"; 
headers += "Content-Length: " + fileSize + "\r\n"; 
headers += "\r\n"; 

Tận hưởng!

1

Tôi nhận ra câu hỏi này giống như 5 tuổi nhưng trong trường hợp một số người khác đang lang thang: ExoPlayer là thư viện của Google cung cấp cho bạn nhiều quyền kiểm soát hơn đối với các tùy chọn như kích thước bộ đệm.

//DefaultUriDataSource – For playing media that can be either local or loaded over the network. 
    DefaultUriDataSource dataSource = new DefaultUriDataSource(WgtechApplication.getAppContext(), 
      Util.getUserAgent(WgtechApplication.getAppContext(), WgtechApplication.class.getSimpleName())); 
    Allocator allocator = new DefaultAllocator(BUFFER_SEGMENT_SIZE); 

    //ExtractorSampleSource – For formats such as MP3, M4A, MP4, WebM, MPEG-TS and AAC. 
    ExtractorSampleSource sampleSource = new ExtractorSampleSource(Uri.parse(RADIO_STREAMING_URL), 
      dataSource, allocator, BUFFER_SEGMENT_COUNT * BUFFER_SEGMENT_SIZE); 
    player.prepare(new MediaCodecAudioTrackRenderer(sampleSource)); 

Đây là một dự án nhỏ mà tôi đã tạo để tìm hiểu cách sử dụng. https://github.com/feresr/MyMediaPlayer

http://developer.android.com/guide/topics/media/exoplayer.html https://github.com/google/ExoPlayer