All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jcodec.movtool.streaming.tracks.TranscodeTrack Maven / Gradle / Ivy

The newest version!
package org.jcodec.movtool.streaming.tracks;
import java.lang.IllegalStateException;
import java.lang.System;
import java.lang.ThreadLocal;

import org.jcodec.common.CodecMeta;
import org.jcodec.common.VideoCodecMeta;
import org.jcodec.common.VideoDecoder;
import org.jcodec.common.VideoEncoder;
import org.jcodec.common.VideoEncoder.EncodedFrame;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Picture;
import org.jcodec.common.model.Rational;
import org.jcodec.common.model.Rect;
import org.jcodec.common.model.Size;
import org.jcodec.movtool.streaming.VirtualPacket;
import org.jcodec.movtool.streaming.VirtualTrack;
import org.jcodec.scale.ColorUtil;
import org.jcodec.scale.Transform;

import java.io.IOException;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;

/**
 * This class is part of JCodec ( www.jcodec.org ) This software is distributed
 * under FreeBSD License
 * 
 * Generic transcode track
 * 
 * @author The JCodec project
 * 
 */
public abstract class TranscodeTrack implements VirtualTrack {

    private static final int TARGET_RATE = 1024;
    private int frameSize;
    private VirtualTrack src;
    private CodecMeta se;
    private ThreadLocal transcoders;
    private int mbW;
    private int mbH;
    private int scaleFactor;
    private int thumbWidth;
    private int thumbHeight;

    protected abstract int getFrameSize(int mbCount, int rate);

    protected abstract VideoDecoder getDecoder(int scaleFactor);

    protected abstract VideoEncoder getEncoder(int rate);

    protected abstract void getCodecPrivate(ByteBuffer buf, Size size);

    public TranscodeTrack(VirtualTrack proresTrack, Size frameDim) {
        this.transcoders = new ThreadLocal();
        this.src = proresTrack;

        scaleFactor = frameDim.getWidth() >= 960 ? 2 : 1;
        thumbWidth = frameDim.getWidth() >> scaleFactor;
        thumbHeight = (frameDim.getHeight() >> scaleFactor) & ~1;

        mbW = (thumbWidth + 15) >> 4;
        mbH = (thumbHeight + 15) >> 4;

        Size size = new Size(thumbWidth, thumbHeight);
        Rational pasp = ((VideoCodecMeta) proresTrack.getCodecMeta()).getPasp();

        ByteBuffer codecPrivate = ByteBuffer.allocate(1024);
        getCodecPrivate(codecPrivate, size);

        se = VideoCodecMeta.createVideoCodecMeta("avc1", codecPrivate, size, pasp);

        frameSize = getFrameSize(mbW * mbH, TARGET_RATE);
        frameSize += frameSize >> 4;
    }

    @Override
    public CodecMeta getCodecMeta() {
        return se;
    }

    @Override
    public VirtualPacket nextPacket() throws IOException {
        VirtualPacket nextPacket = src.nextPacket();
        if (nextPacket == null)
            return null;
        return new TranscodePacket(this, nextPacket);
    }

    private static class TranscodePacket extends VirtualPacketWrapper {
        private TranscodeTrack track;

        public TranscodePacket(TranscodeTrack track, VirtualPacket nextPacket) {
            super(nextPacket);
            this.track = track;
        }

        @Override
        public int getDataLen() {
            return track.frameSize;
        }

        @Override
        public ByteBuffer getData() throws IOException {
            Transcoder t = track.transcoders.get();
            if (t == null) {
                t = new Transcoder(track);
                track.transcoders.set(t);
            }
            ByteBuffer buf = ByteBuffer.allocate(track.frameSize);
            ByteBuffer data = src.getData();
            return t.transcodeFrame(data, buf);
        }
    }

    static class Transcoder {
        private VideoDecoder decoder;
        private VideoEncoder[] encoder;
        private Picture pic0;
        private Picture pic1;
        private Transform transform;
        private TranscodeTrack track;

        public Transcoder(TranscodeTrack track) {
            this.encoder = new VideoEncoder[3];
            this.track = track;
            this.decoder = track.getDecoder(track.scaleFactor);
            this.encoder[0] = track.getEncoder(TARGET_RATE);
            this.encoder[1] = track.getEncoder((int) (TARGET_RATE * 0.9));
            this.encoder[2] = track.getEncoder((int) (TARGET_RATE * 0.8));
            pic0 = Picture.create(track.mbW << 4, track.mbH << 4, ColorSpace.YUV444);
        }

        public ByteBuffer transcodeFrame(ByteBuffer src, ByteBuffer dst) {
            Picture decoded = decoder.decodeFrame(src, pic0.getData());
            if (pic1 == null) {
                pic1 = Picture.create(decoded.getWidth(), decoded.getHeight(), ColorSpace.YUV420);
                transform = ColorUtil.getTransform(decoded.getColor(), ColorSpace.YUV420);
            }
            transform.transform(decoded, pic1);
            pic1.setCrop(new Rect(0, 0, track.thumbWidth, track.thumbHeight));
            // Rate control, reinforcement mechanism
            for (int i = 0; i < encoder.length; i++) {
                try {
                    dst.clear();
                    EncodedFrame out = encoder[i].encodeFrame(pic1, dst);
                    break;
                } catch (BufferOverflowException ex) {
                    System.out.println("Abandon frame!!!");
                }
            }

            return dst;
        }
    }

    @Override
    public void close() throws IOException {
        src.close();
    }

    @Override
    public VirtualEdit[] getEdits() {
        return src.getEdits();
    }

    @Override
    public int getPreferredTimescale() {
        return src.getPreferredTimescale();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy