12

我正在尝试使用Xuggler(我相信它在幕后使用ffmpeg)来执行以下操作:

  • 接受原始 MPJPEG 视频比特流(来自小型 TTL 串行相机)并将其编码/转码为 h.264;和
  • 接受原始音频比特流(来自麦克风)并将其编码为 AAC;然后
  • 将两个(音频和视频)比特流混合到一个 MPEG-TS 容器中

我已经观看/阅读了他们的一些优秀教程,到目前为止,这就是我所拥有的:

// I'll worry about implementing this functionality later, but
// involves querying native device drivers.
byte[] nextMjpeg = getNextMjpegFromSerialPort();

// I'll also worry about implementing this functionality as well;
// I'm simply providing these for thoroughness.
BufferedImage mjpeg = MjpegFactory.newMjpeg(nextMjpeg);

// Specify a h.264 video stream (how?)
String h264Stream = "???";

IMediaWriter writer = ToolFactory.makeWriter(h264Stream);
writer.addVideoStream(0, 0, ICodec.ID.CODEC_ID_H264);
writer.encodeVideo(0, mjpeg);

一方面,我想我已经接近了,但它仍然不正确;而且我只是通过阅读视频代码示例(而不是音频-我找不到任何好的音频示例)来做到这一点。

从字面上看,我将对进入我的 Xuggler 实现的原始视频和音频提要进行字节级访问。但是对于我的生活,我无法弄清楚如何将它们变成 h.264/AAC/MPEG-TS 格式。在此先感谢您的任何帮助。

4

2 回答 2

16

查看Xuggler 这个示例代码,以下应该可以将视频编码为 H.264 并将其复用到 MPEG2TS 容器中:

IMediaWriter writer = ToolFactory.makeWriter("output.ts");
writer.addVideoStream(0, 0, ICodec.ID.CODEC_ID_H264, width, height);
for (...)
{

   BufferedImage mjpeg = ...;

   writer.encodeVideo(0, mjpeg);
}

容器类型是从文件扩展名中猜测出来的,编解码器是明确指定的。

要复用音频和视频,您可以执行以下操作:

writer.addVideoStream(videoStreamIndex, 0, videoCodec, width, height);
writer.addAudioStream(audioStreamIndex, 0, audioCodec, channelCount, sampleRate);

while (... have more data ...)
{
    BufferedImage videoFrame = ...;
    long videoFrameTime = ...; // this is the time to display this frame
    writer.encodeVideo(videoStreamIndex, videoFrame, videoFrameTime, DEFAULT_TIME_UNIT);

    short[] audioSamples = ...; // the size of this array should be number of samples * channelCount
    long audioSamplesTime = ...; // this is the time to play back this bit of audio
    writer.encodeAudio(audioStreamIndex, audioSamples, audioSamplesTime, DEFAULT_TIME_UNIT);
}

在这种情况下,我相信您的代码负责交错音频和视频:您希望在每次通过循环时调用 encodeAudio()encodeVideo(),具体取决于可用的数据(一大块音频样本或视频帧) ) 具有较早的时间戳

您可能最终会使用另一个基于IStreamCoder的较低级别的 API ,它可以更好地控制各种参数。我认为您不需要使用它。

要回答您提出的具体问题:

(1) “将 BufferedImage (M/JPEG) 编码为 h.264 流” - 你已经明白了,writer.addVideoStream(..., ICodec.ID.CODEC_ID_H264)确保你得到 H.264编解码器。要获取传输流 (MPEG2 TS)容器,只需makeWriter()使用扩展名为 .ts 的文件名进行调用。

(2)“弄清楚原始音频馈送的“BufferedImage-equivalent”是什么” - 这是一个短 [] 或IAudioSamples对象(两者似乎都有效,但 IAudioSamples 必须从一个 IBuffer 构造,这很多不那么直截了当)。

(3)“将此音频类编码成AAC音频流”——调用writer.addAudioStream(..., ICodec.ID.CODEC_ID_AAC, channelCount, sampleRate)

(4) “将两个流多路复用到同一个 MPEG-TS 容器中” -makeWriter()使用 .ts 文件名调用,它设置容器类型。为了正确的音频/视频同步,您可能需要以正确的顺序调用 encodeVideo()/encodeAudio()。

PS 总是先通过最早可用的音频/视频。例如,如果您有 440 个样本长的音频块(在 44000 Hz 采样率下,440 / 44000 = 0.01 秒),而视频正好是 25fps(1 / 25 = 0.04 秒),您可以将它们提供给作者这个命令:

video0 @ 0.00 sec
audio0 @ 0.00 sec
audio1 @ 0.01 sec
audio2 @ 0.02 sec
audio3 @ 0.03 sec
video1 @ 0.04 sec
audio4 @ 0.04 sec
audio5 @ 0.05 sec

...等等

只要连续的音频/视频时间戳相对接近,大多数播放设备可能都可以使用流,但这是您为完美多路复用器所做的。

PS 您可能需要参考一些文档:Xuggler 类图ToolFactoryIMediaWriterICodec

于 2012-12-18T14:09:10.860 回答
0

我认为您应该查看 gstreamer:http://gstreamer.freedesktop.org/您必须寻找可以捕获相机输入的插件,然后将其通过管道传输到 libx264 和 aac 插件,然后它们通过 mpegts 多路复用器传递它们。

gstreamer 中的管道如下所示:

v4l2src queue-size=15 ! video/x-raw,framerate=25/1,width=384,height=576 ! \
  avenc_mpeg4 name=venc \
alsasrc ! audio/x-raw,rate=48000,channels=1 ! audioconvert ! lamemp3enc name=aenc \
avimux name=mux ! filesink location=rec.avi venc. ! mux. aenc. ! mux.

在这个管道中,使用了 mpeg4 和 mp3 编码器,并且流被复用为 avi。您应该能够找到 libx264 和 aac 的插件。如果您需要进一步的指示,请告诉我。

于 2012-12-15T14:45:26.417 回答