【问题标题】:Java - h.264 video encodingJava - h.264 视频编码
【发布时间】:2014-01-27 01:35:11
【问题描述】:

我正在尝试制作一个可以对 h.264 视频进行编码和解码的程序,以便我可以编辑此视频。 如果我想用java制作这个程序,谁能告诉我怎么做?

【问题讨论】:

  • 最终FFMpeg 可以做到这一点,但我不太确定。如果是这样,那么肯定有一个 Java Wrapper。
  • 试试Xuggler API
  • 现在你提到它,这似乎是一个更好的主意(比尝试使用 JMF)。 +1
  • Xuggler 于 2012 年去世,取而代之的是 humble-video

标签: java h.264 video-encoding


【解决方案1】:

您可以使用 JCodec (http://jcodec.org)。

解码视频序列:

int frameNumber = 10000;
FileChannelWrapper ch = null;
try {
    ch = NIOUtils.readableFileChannel(new File(path to mp4));
    FrameGrab frameGrab = new FrameGrab(ch);

    frameGrab.seek(frameNumber);
    Picture frame;
    for (int i = 0; (frame = frameGrab.getNativeFrame()) != null && i < 200; i++) {
        // Do something
    }
} finally {
    NIOUtils.closeQuietly(ch);
}

编码一个序列:

public class SequenceEncoder {
    private SeekableByteChannel ch;
    private Picture toEncode;
    private RgbToYuv420 transform;
    private H264Encoder encoder;
    private ArrayList<ByteBuffer> spsList;
    private ArrayList<ByteBuffer> ppsList;
    private CompressedTrack outTrack;
    private ByteBuffer _out;
    private int frameNo;
    private MP4Muxer muxer;

    public SequenceEncoder(File out) throws IOException {
        this.ch = NIOUtils.writableFileChannel(out);

        // Transform to convert between RGB and YUV
        transform = new RgbToYuv420(0, 0);

        // Muxer that will store the encoded frames
        muxer = new MP4Muxer(ch, Brand.MP4);

        // Add video track to muxer
        outTrack = muxer.addTrackForCompressed(TrackType.VIDEO, 25);

        // Allocate a buffer big enough to hold output frames
        _out = ByteBuffer.allocate(1920 * 1080 * 6);

        // Create an instance of encoder
        encoder = new H264Encoder();

        // Encoder extra data ( SPS, PPS ) to be stored in a special place of
        // MP4
        spsList = new ArrayList<ByteBuffer>();
        ppsList = new ArrayList<ByteBuffer>();

    }

    public void encodeImage(Picture bi) throws IOException {
        if (toEncode == null) {
            toEncode = Picture.create(bi.getWidth(), bi.getHeight(), ColorSpace.YUV420);
        }

        // Perform conversion ( RGB -> YUV )
        transform.transform(bi, toEncode);

        // Encode image into H.264 frame, the result is stored in '_out' buffer
        _out.clear();
        ByteBuffer result = encoder.encodeFrame(_out, toEncode);

        // Based on the frame above form correct MP4 packet
        spsList.clear();
        ppsList.clear();
        H264Utils.encodeMOVPacket(result, spsList, ppsList);

        // Add packet to video track
        outTrack.addFrame(new MP4Packet(result, frameNo, 25, 1, frameNo, true, null, frameNo, 0));

        frameNo++;
    }

    public void finish() throws IOException {
        // Push saved SPS/PPS to a special storage in MP4
        outTrack.addSampleEntry(H264Utils.createMOVSampleEntry(spsList, ppsList));

        // Write MP4 header and finalize recording
        muxer.writeHeader();
        NIOUtils.closeQuietly(ch);
    }
}

最后转换 TOFROM Android 图像使用:

public static Picture fromBitmap(Bitmap src) {
    Picture dst = Picture.create((int)src.getWidth(), (int)src.getHeight(), RGB);
    fromBitmap(src, dst);
    return dst;
}

public static void fromBitmap(Bitmap src, Picture dst) {
    int[] dstData = dst.getPlaneData(0);
    int[] packed = new int[src.getWidth() * src.getHeight()];

    src.getPixels(packed, 0, src.getWidth(), 0, 0, src.getWidth(), src.getHeight());

    for (int i = 0, srcOff = 0, dstOff = 0; i < src.getHeight(); i++) {
        for (int j = 0; j < src.getWidth(); j++, srcOff++, dstOff += 3) {
            int rgb = packed[srcOff];
            dstData[dstOff]     = (rgb >> 16) & 0xff;
            dstData[dstOff + 1] = (rgb >> 8) & 0xff;
            dstData[dstOff + 2] = rgb & 0xff;
        }
    }
}

public static Bitmap toBitmap(Picture src) {
    Bitmap dst = Bitmap.create(pic.getWidth(), pic.getHeight(), ARGB_8888);
    toBitmap(src, dst);
    return dst;
}

public static void toBitmap(Picture src, Bitmap dst) {
    int[] srcData = src.getPlaneData(0);
    int[] packed = new int[src.getWidth() * src.getHeight()];

    for (int i = 0, dstOff = 0, srcOff = 0; i < src.getHeight(); i++) {
        for (int j = 0; j < src.getWidth(); j++, dstOff++, srcOff += 3) {
            packed[dstOff] = (srcData[srcOff] << 16) | (srcData[srcOff + 1] << 8) | srcData[srcOff + 2];
        }
    }
    dst.setPixels(packed, 0, src.getWidth(), 0, 0, src.getWidth(), src.getHeight());
}

终于解码你会得到 YUV 帧,为了将其转换为 RGB 帧去:

Transform transform = ColorUtil.getTransform(pic.getColor(), ColorSpace.RGB);
Picture rgb = Picture.create(pic.getWidth(), pic.getHeight(), ColorSpace.RGB);
transform.transform(pic, rgb);

并确保您的下载 JAR 包含所有部门:http://jcodec.org/downloads/jcodec-0.1.3-uberjar.jar

【讨论】:

  • 感谢您的回答。编码操作的计算密集度如何?
【解决方案2】:

获取或编写一个可以对所需格式进行编码和解码的类。确保它是一个服务提供者接口。将其添加到应用程序的运行时类路径中。使用 JMF 的默认播放器播放它。不太确定是否要编辑它。

【讨论】:

    【解决方案3】:

    有一个可用于 VLC 的 Java 包装器:http://www.capricasoftware.co.uk/projects/vlcj/

    【讨论】:

      【解决方案4】:

      检查 ffmpeg 库和 x264。编译和组合这些库。它具有非常好的视频编码/解码 API。如果仅编译 ffmpeg,则无法编码 h.264,只能解码。要对 h264 进行编码,您需要编译 x264 库。 http://ubuntuforums.org/showthread.php?t=786095。很不错的教程。 这是 C API,但您可以使用本机方法来调用它。

      最好的问候,对不起我的英语。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-11-23
        • 2011-10-30
        • 2013-06-06
        • 2011-01-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多