通过LocalSocket从MediaRecorder流式传输视频

6
我将尝试从Android的MediaRecorder通过本地Socket发送h264/AAC视频。目标是通过RTMP或RTSP向WOWZA服务器发送视频,但目前我遇到了很多麻烦,所以我只是试图将数据从LocalServerSocket写入文件。
以下是部分代码。很抱歉它不是很整洁,但我花了很多时间测试许多东西,我的项目现在一团糟。
在Camera活动中,设置输出文件:
LocalSocket outSocket = new LocalSocket();

try {
    outSocket.connect(new LocalSocketAddress(LOCAL_SOCKET));
} catch (Exception e) {
    Log.i(LOG_TAG, "Error connecting socket: "+e);
}
mMediaRecorder.setOutputFile(outSocket.getFileDescriptor());

本地服务器套接字实现:
try {
    mLocalServerSocket = new LocalServerSocket(mName);
} catch (Exception e) {
    Log.e(LOG_TAG, "Error creating server socket: "+e);
    return;
}

while (true) {

    File out = null;
    FileOutputStream fop = null;
    try {
        mLocalClientSocket = mLocalServerSocket.accept();

        InputStream in = mLocalClientSocket.getInputStream();

        out = new File(mContext.getExternalFilesDir(null), "testfile.mp4");
        fop = new FileOutputStream(out);

        int len = 0;
        byte[] buffer = new byte[1024];
        while ((len = in.read(buffer)) >= 0) {

            Log.i(LOG_TAG, "Writing "+len+" bytes");
            fop.write(buffer, 0, len);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    finally{
        try {
            fop.close();
            mLocalClientSocket.close();
        } catch (Exception e2) {}
    }
}

问题在于生成的文件无法被任何媒体播放器读取。您认为这是由于编码问题引起的吗?如果我理解正确,这段代码应该生成一个二进制文件?!提前感谢,祝一切顺利。

如果您没有收到错误提示,那可能是由于编码或文件名问题引起的。您设置了什么OutputFormat?也许将其重命名为.3gp等格式会有所帮助-请参见此处 - zapl
输出格式为MPEG_4,所以我猜应该可以使用.mp4扩展名。此外,如果我直接写入到.m4p文件中,在mMediaRecorder.setOutputFile()中它可以完美地工作。 - Simon
你是否检查过像mediainfo这样的工具是否能够读取所创建视频的属性?你也可以尝试对比直接生成的文件和通过本地套接字发送的文件的二进制差异(至少是头部),以检查生成的文件是否正确。 - zapl
我没有尝试使用mediainfo,但是我的媒体播放器都无法读取文件或找到任何编解码器信息(已尝试使用VLC、MplayerX和Quicktime)。 我还尝试比较了这些文件。工作正常的文件以以下方式开头:ftyp3gp43gp43gp6wideBtmdat而不工作的文件则以以下方式开头:ftyp3gp43gp43gp6widemdat至于其他部分,我正在处理中,但目前我不能说是否存在问题。 - Simon
根据http://www.mattakis.com/blog/kisg/20090708/broadcasting-video-with-android-without-writing-to-the-file-system,我的问题应该与头部不正确设置有关,因为套接字不可寻址。现在正在尝试找到解决方案。 - Simon
3个回答

2

好的,我已经找到为什么文件无法播放的原因。在MP4和3GPP文件中,有一个包含以下字节的头:

ftyp3gp4 3gp43gp6 wide mdat

以十六进制表示为

0000001866747970336770340000030033677034336770360000000877696465000392D86D6461740000

'mdat'标签之前的4个字节表示位于文件末尾的另一个'moov'标签的位置。这个位置通常在录制结束时设置,但由于MediaRecorder无法查找sockets,所以在我们的情况下无法将这些字节设置为正确的值。

我的问题现在是找到一种使这样的文件可流式传输的方法,因为它需要在录制结束之前播放。


3
你已经找到了使它可以流媒体播放的解决方案吗? - Houf
嗨,Simon,你找到了解决方案吗?请分享一下,我也遇到了同样的问题。 - ankita gahoi
嗨,Ankita。很抱歉,我们暂时放弃了这个项目,所以我停止了解决这个问题的工作。祝你好运,加油。 - Simon
3
大家好,这是一个良好流媒体实现的示例:https://github.com/fyhertz/libstreaming - Felipe Leusin

1
你可以尝试使用mp4box来重组你的文件。moov盒子为每个音频和视频样本提供索引。如果它在文件末尾,那么就很难进行流媒体传输。
这可能会有所帮助: http://boliston.wordpress.com/tag/moov-box/ 或者这样做: mp4box -inter 0.5 some_file.mp4
(我目前没有机会尝试)
如果你需要让它与你的应用程序一起工作,我不知道是否有任何将mp4box移植到Android的活动。

顺便提一下,现在mp4box是GPAC项目的一部分。至少在Ubuntu中有一个相关的软件包。http://gpac.wp.mines-telecom.fr/ - AOSP_junkie

0
今天我尝试做同样的事情,但是mp4不太容易流式传输(如前所述,一些部分是在结尾处编写的)。我不是说这是不可能的,但至少似乎相当困难。
因此,新的Android API(4.3)的解决方法可能是这个:
  • 将相机预览设置为SurfaceTexture:camera.setPreviewTexture
  • 使用OpenGL和MediaCodex + Muxer记录此纹理
这种解决方案的缺点是相机的预览大小可能小于视频大小。这意味着根据您的设备,您无法以最高分辨率录制。提示:有些相机会说它们不支持更高的预览大小,但实际上它们可以尝试配置相机将预览大小设置为视频大小。如果这样做,请捕获camera.setParameters的RuntimeException,如果失败,则仅使用支持的预览大小。
一些关于如何从SurfaceTexture录制的链接:

Bigflage:MediaCodec内容的很好的示例。

Lablet的VideoRecorder类可能也会有用。

另外一个可能有用的是spydroid-ipcamera,它可以将MediaRecorder套接字中的数据流作为RTP流进行传输,但我还没有找到将其馈送到MediaCodec的方法。(我已经卡在了正确读取NAL单元大小的问题上...)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接