Java環境でH.264ビデオフレームをデコードする方法を知っている人はいますか?
私のネットワークカメラ製品はRTP/RTSPストリーミングをサポートしています。
ネットワークカメラからのサービス標準RTP/RTSPが提供され、「RTP /RTSPoverHTTP」もサポートしています。
RTSP:TCP 554 RTP開始ポート:UDP 5000
またはXugglerを使用します。RTP、RTMP、HTTP、またはその他のプロトコルで動作し、H264 およびその他のほとんどのコーデックをデコードおよびエンコードできます。また、積極的にメンテナンスされ、無料でオープンソース (LGPL) になっています。
「JNI + ffmpeg」を使用するのが最善の解決策だと思います。私の現在のプロジェクトでは、libgdx に基づく Java openGL ゲームで同時に複数のフルスクリーン ビデオを再生する必要があります。ほとんどすべての無料のライブラリを試しましたが、どれも許容できるパフォーマンスではありませんでした。最終的に、ffmpeg で動作するように独自の jni C コードを作成することにしました。これが私のラップトップでの最終的なパフォーマンスです。
最初のバージョンを完成させるのに数日しかかかりませんでした。しかし、最初のバージョンのデコード速度は約 120FPS で、アップロード時間は 1 フレームあたり約 5 ミリ秒でした。数か月の最適化の後、この最終的なパフォーマンスといくつかの追加機能が得られました。これで、遅延なく複数の HD ビデオを同時に再生できるようになりました。
私のゲームのほとんどのビデオの背景は透明です。この種類の透過ビデオは、2 つのビデオ ストリームを含む mp4 ファイルです。一方のストリームには h264rgb でエンコードされた RGB データが格納され、もう一方のストリームには h264 でエンコードされたアルファ データが格納されます。そのため、アルファ ビデオを再生するには、2 つのビデオ ストリームをデコードしてマージし、GPU にアップロードする必要があります。その結果、ゲーム内で不透明な HD ビデオの上に複数の透明な HD ビデオを同時に再生できます。
JCodec ( http://jcodec.org ) と呼ばれる純粋な Java ライブラリを使用できます。
1 つの H.264 フレームのデコードは次のように簡単です。
ByteBuffer bb = ... // Your frame data is stored in this buffer
H264Decoder decoder = new H264Decoder();
Picture out = Picture.create(1920, 1088, ColorSpace.YUV_420); // Allocate output frame of max size
Picture real = decoder.decodeFrame(bb, out.getData());
BufferedImage bi = JCodecUtil.toBufferedImage(real); // If you prefere AWT image
コンテナー ( MP4 など) から読み取りたい場合は、便利なヘルパー クラス FrameGrab を使用できます。
int frameNumber = 150;
BufferedImage frame = FrameGrab.getFrame(new File("filename.mp4"), frameNumber);
ImageIO.write(frame, "png", new File("frame_150.png"));
最後に、洗練された完全なサンプルを次に示します。
private static void avc2png(String in, String out) throws IOException {
SeekableByteChannel sink = null;
SeekableByteChannel source = null;
try {
source = readableFileChannel(in);
sink = writableFileChannel(out);
MP4Demuxer demux = new MP4Demuxer(source);
H264Decoder decoder = new H264Decoder();
Transform transform = new Yuv420pToRgb(0, 0);
MP4DemuxerTrack inTrack = demux.getVideoTrack();
VideoSampleEntry ine = (VideoSampleEntry) inTrack.getSampleEntries()[0];
Picture target1 = Picture.create((ine.getWidth() + 15) & ~0xf, (ine.getHeight() + 15) & ~0xf,
ColorSpace.YUV420);
Picture rgb = Picture.create(ine.getWidth(), ine.getHeight(), ColorSpace.RGB);
ByteBuffer _out = ByteBuffer.allocate(ine.getWidth() * ine.getHeight() * 6);
BufferedImage bi = new BufferedImage(ine.getWidth(), ine.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
AvcCBox avcC = Box.as(AvcCBox.class, Box.findFirst(ine, LeafBox.class, "avcC"));
decoder.addSps(avcC.getSpsList());
decoder.addPps(avcC.getPpsList());
Packet inFrame;
int totalFrames = (int) inTrack.getFrameCount();
for (int i = 0; (inFrame = inTrack.getFrames(1)) != null; i++) {
ByteBuffer data = inFrame.getData();
Picture dec = decoder.decodeFrame(splitMOVPacket(data, avcC), target1.getData());
transform.transform(dec, rgb);
_out.clear();
AWTUtil.toBufferedImage(rgb, bi);
ImageIO.write(bi, "png", new File(format(out, i)));
if (i % 100 == 0)
System.out.println((i * 100 / totalFrames) + "%");
}
} finally {
if (sink != null)
sink.close();
if (source != null)
source.close();
}
}
Java Media Framework (JMF) を見てみましょう - http://java.sun.com/javase/technologies/desktop/media/jmf/2.1.1/formats.html
少し前に使っていたので少し未熟でしたが、その後強化されたのかもしれません。