/*
This file is part of jpcsp.
Jpcsp is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Jpcsp is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Jpcsp. If not, see <http://www.gnu.org/licenses/>.
*/
package jpcsp.media.codec.h264;
import org.apache.log4j.Logger;
import com.twilight.h264.decoder.AVFrame;
import com.twilight.h264.decoder.AVPacket;
import com.twilight.h264.decoder.AVRational;
import com.twilight.h264.decoder.MpegEncContext;
import jpcsp.media.codec.IVideoCodec;
public class H264Decoder implements IVideoCodec {
private static Logger log = Logger.getLogger("h264");
private MpegEncContext context;
private AVFrame picture;
private AVPacket packet;
private final int gotPicture[] = new int[1];
private AVRational aspectRatio;
@Override
public int init(int extraData[]) {
context = MpegEncContext.avcodec_alloc_context();
picture = AVFrame.avcodec_alloc_frame();
packet = new AVPacket();
packet.av_init_packet();
if (extraData != null) {
context.extradata_size = extraData.length;
// Add 4 additional values to avoid exceptions while parsing
int[] extraDataPlus4 = new int[context.extradata_size + 4];
System.arraycopy(extraData, 0, extraDataPlus4, 0, context.extradata_size);
context.extradata = extraDataPlus4;
}
int result = context.avcodec_open(new com.twilight.h264.decoder.H264Decoder());
if (result < 0) {
return result;
}
gotPicture[0] = 0;
return 0;
}
@Override
public int decode(int input[], int inputOffset, int inputLength) {
packet.data_base = input;
packet.data_offset = inputOffset;
packet.size = inputLength;
int consumedLength;
try {
consumedLength = context.avcodec_decode_video2(picture, gotPicture, packet);
} catch (ArrayIndexOutOfBoundsException e) {
log.error("H264Decoder.decode", e);
return -1;
}
if (consumedLength < 0) {
log.error(String.format("H264 decode error 0x%08X", consumedLength));
gotPicture[0] = 0;
return consumedLength;
}
if (hasImage()) {
context.priv_data.displayPicture.copyTo(picture);
aspectRatio = context.sample_aspect_ratio;
}
return consumedLength;
}
@Override
public boolean hasImage() {
return gotPicture[0] != 0;
}
@Override
public int getImageWidth() {
return context.width;
}
@Override
public int getImageHeight() {
return context.height;
}
@Override
public boolean isKeyFrame() {
return picture.key_frame != 0;
}
@Override
public void getAspectRatio(int[] numDen) {
numDen[0] = aspectRatio.num;
numDen[1] = aspectRatio.den;
}
@Override
public int getImage(int[] luma, int[] cb, int[] cr) {
int width = getImageWidth();
int height = getImageHeight();
int width2 = width >> 1;
int height2 = height >> 1;
// Copy luma component: width * height values
for (int y = 0; y < height; y++) {
System.arraycopy(picture.data_base[0], y * picture.linesize[0] + picture.data_offset[0], luma, y * width , width );
}
// Copy Cb and Cr components: width2 * height2 values each
for (int y = 0; y < height2; y++) {
System.arraycopy(picture.data_base[1], y * picture.linesize[1] + picture.data_offset[1], cb , y * width2, width2);
System.arraycopy(picture.data_base[2], y * picture.linesize[2] + picture.data_offset[2], cr , y * width2, width2);
}
return 0;
}
}