Skip to content
Permalink
Browse files

* Allocate native memory for `Frame` using `Pointer` to allow deallo…

…cation with `PointerScope` (issue #1152)
  • Loading branch information...
saudet committed Mar 12, 2019
1 parent b1c373b commit 924f1d9ee30e0d35455cd32dbbb6711f94ab3698
@@ -1,4 +1,6 @@

* Allocate native memory for `Frame` using `Pointer` to allow deallocation with `PointerScope` ([issue #1152](https://github.com/bytedeco/javacv/issues/1152))
* Add `module-info.java` and depend on modularized JavaCPP Presets to comply with JPMS
* Upgrade dependencies for FFmpeg 4.1.1 and libfreenect 0.5.7
* Allow allocation of `Frame` images with custom strides
* Take into account `Bitmap.getRowBytes()` in `AndroidFrameConverter.convert(Bitmap)` ([issue #1143](https://github.com/bytedeco/javacv/issues/1143))
@@ -320,13 +320,13 @@

converter.frame = null;
Frame frame1 = converter.convert(pix);
assertEquals(frame1.opaque, pix);
// assertEquals(frame1.opaque, pix);

PIX pix2 = PIX.createHeader(pix.w(), pix.h(), pix.d()).data(pix.data()).wpl(pix.wpl());
assertNotEquals(pix, pix2);

Frame frame2 = converter.convert(pix2);
assertEquals(frame2.opaque, pix2);
// assertEquals(frame2.opaque, pix2);

IntBuffer frameBuf = ((ByteBuffer)frame.image[0].position(0)).asIntBuffer();
IntBuffer frame1Buf = ((ByteBuffer)frame1.image[0].position(0)).asIntBuffer();
@@ -94,26 +94,35 @@ public void testFFmpegFrameGrabber() {
int n = 0, m = 0;
Frame frame2;
while ((frame2 = grabber.grab()) != null) {
Frame clone2 = frame2.clone();
if (frame2.image != null) {
Frame frame = frames[n++];
assertEquals(frame.imageWidth, frame2.imageWidth);
assertEquals(frame.imageHeight, frame2.imageHeight);
assertEquals(frame.imageChannels, frame2.imageChannels);
assertEquals(frame.imageWidth, clone2.imageWidth);
assertEquals(frame.imageHeight, clone2.imageHeight);
assertEquals(frame.imageChannels, clone2.imageChannels);

UByteIndexer frameIdx = frame.createIndexer();
UByteIndexer frame2Idx = frame2.createIndexer();
UByteIndexer clone2Idx = clone2.createIndexer();
for (int i = 0; i < frameIdx.rows(); i++) {
for (int j = 0; j < frameIdx.cols(); j++) {
for (int k = 0; k < frameIdx.channels(); k++) {
int b = frameIdx.get(i, j, k);
assertEquals(b, frame2Idx.get(i, j, k));
assertEquals(b, clone2Idx.get(i, j, k));
}
}
}
} else {
FloatBuffer audioBuffer2 = (FloatBuffer)frame2.samples[0];
FloatBuffer cloneBuffer2 = (FloatBuffer)clone2.samples[0];
while (audioBuffer2.hasRemaining()) {
assertEquals((float)audioBuffer.get(m++) / (Short.MAX_VALUE + 1), audioBuffer2.get(), 0);
assertEquals((float)audioBuffer.get(m) / (Short.MAX_VALUE + 1), audioBuffer2.get(), 0);
assertEquals((float)audioBuffer.get(m) / (Short.MAX_VALUE + 1), cloneBuffer2.get(), 0);
m++;
}
}
}
@@ -289,9 +298,9 @@ public void testFFmpegFrameGrabberSeeking() throws IOException {
recorder.setAudioQuality(0);
recorder.start();
if (seektestnum!=2) {
Frame frame = new Frame(640, 480, Frame.DEPTH_UBYTE, 3);
UByteIndexer frameIdx = frame.createIndexer();
for (int n = 0; n < 10000; n++) {
Frame frame = new Frame(640, 480, Frame.DEPTH_UBYTE, 3);
UByteIndexer frameIdx = frame.createIndexer();
for (int i = 0; i < frameIdx.rows(); i++) {
for (int j = 0; j < frameIdx.cols(); j++) {
for (int k = 0; k < frameIdx.channels(); k++) {
@@ -627,14 +627,14 @@ public Frame pullImage() throws Exception {
} else {
frame.imageStride = frame.imageWidth;
int size = av_image_get_buffer_size(filt_frame.format(), frame.imageWidth, frame.imageHeight, 1);
// Fix bug on Android4.0,check out https://github.com/bytedeco/javacpp/issues/39
if (image_buf[0] == null || image_buf[0].capacity() < size) {
image_buf[0] = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
if (image_ptr[0] == null || image_ptr[0].capacity() < size) {
image_ptr[0] = new BytePointer(size);
image_buf[0] = image_ptr[0].asBuffer();
}
frame.image = image_buf;
frame.image[0].position(0).limit(size);
frame.imageChannels = (size + frame.imageWidth * frame.imageHeight - 1) / (frame.imageWidth * frame.imageHeight);
ret = av_image_copy_to_buffer(new BytePointer((ByteBuffer) frame.image[0].position(0)), frame.image[0].capacity(),
ret = av_image_copy_to_buffer(image_ptr[0].position(0), (int)image_ptr[0].capacity(),
new PointerPointer(filt_frame), filt_frame.linesize(), filt_frame.format(), frame.imageWidth, frame.imageHeight, 1);
}
return frame;
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015 Samuel Audet
* Copyright (C) 2015-2019 Samuel Audet
*
* Licensed either under the Apache License, Version 2.0, or (at your option)
* under the terms of the GNU General Public License as published by
@@ -24,7 +24,6 @@

import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
@@ -37,6 +36,7 @@
import org.bytedeco.javacpp.FloatPointer;
import org.bytedeco.javacpp.IntPointer;
import org.bytedeco.javacpp.LongPointer;
import org.bytedeco.javacpp.Pointer;
import org.bytedeco.javacpp.ShortPointer;
import org.bytedeco.javacpp.indexer.ByteIndexer;
import org.bytedeco.javacpp.indexer.DoubleIndexer;
@@ -94,7 +94,7 @@
/** Buffers to hold audio samples from multiple channels for an audio frame. */
public Buffer[] samples;

/** The underlying data object, for example, AVFrame, IplImage, or Mat. */
/** The underlying data object, for example, Pointer, AVFrame, IplImage, or Mat. */
public Object opaque;

/** Timestamp of the frame creation. */
@@ -120,7 +120,8 @@ public Frame(int width, int height, int depth, int channels, int imageStride) {
this.imageStride = imageStride;
this.image = new Buffer[1];

ByteBuffer buffer = ByteBuffer.allocateDirect(imageHeight * imageStride * pixelSize(depth)).order(ByteOrder.nativeOrder());
Pointer pointer = new BytePointer(imageHeight * imageStride * pixelSize(depth));
ByteBuffer buffer = pointer.asByteBuffer();
switch (imageDepth) {
case DEPTH_BYTE:
case DEPTH_UBYTE: image[0] = buffer; break;
@@ -132,6 +133,7 @@ public Frame(int width, int height, int depth, int channels, int imageStride) {
case DEPTH_DOUBLE: image[0] = buffer.asDoubleBuffer(); break;
default: throw new UnsupportedOperationException("Unsupported depth value: " + imageDepth);
}
opaque = pointer;
}

/** Returns {@code createIndexer(true, 0)}. */
@@ -198,49 +200,52 @@ public Frame(int width, int height, int depth, int channels, int imageStride) {
public Frame clone() {
Frame newFrame = new Frame();


// Video part
newFrame.imageWidth = imageWidth;
newFrame.imageHeight = imageHeight;
newFrame.imageDepth = imageDepth;
newFrame.imageChannels = imageChannels;
newFrame.imageStride = imageStride;
newFrame.keyFrame = keyFrame;
newFrame.opaque = opaque;
newFrame.image = cloneBufferArray(image);
newFrame.opaque = new Pointer[2];
if (image != null) {
newFrame.image = new Buffer[image.length];
((Pointer[])newFrame.opaque)[0] = cloneBufferArray(image, newFrame.image);
}

// Audio part
newFrame.audioChannels = audioChannels;
newFrame.sampleRate = sampleRate;
newFrame.samples = cloneBufferArray(samples);
if (samples != null) {
newFrame.samples = new Buffer[samples.length];
((Pointer[])newFrame.opaque)[1] = cloneBufferArray(samples, newFrame.samples);
}

// Add timestamp
newFrame.timestamp = timestamp;

return newFrame;

}

/**
* This private method takes a buffer array as input and returns a deep copy.
* It is assumed that all buffers in the input array are of the same subclass.
*
* @param srcBuffers - Buffer array to be cloned
* @return New buffer array
* @param clonedBuffers - Buffer array to fill with clones
* @return Opaque object to store
*
* @author Extension proposed by Dragos Dutu
*/
private static Buffer[] cloneBufferArray(Buffer[] srcBuffers) {

Buffer[] clonedBuffers = null;
int i;
short dataSize;
private static Pointer cloneBufferArray(Buffer[] srcBuffers, Buffer[] clonedBuffers) {
Pointer opaque = null;

if (srcBuffers != null) {
clonedBuffers = new Buffer[srcBuffers.length];

for (i = 0; i < srcBuffers.length; i++)
if (srcBuffers != null && srcBuffers.length > 0) {
int totalCapacity = 0;
for (int i = 0; i < srcBuffers.length; i++) {
srcBuffers[i].rewind();
totalCapacity += srcBuffers[i].capacity();
}

/*
* In order to optimize the transfer we need a type check.
@@ -261,45 +266,63 @@ public Frame clone() {
*
*/

if (srcBuffers[0] instanceof ByteBuffer)
// dataSize is 1
for (i = 0; i < srcBuffers.length; i++)
clonedBuffers[i] = ByteBuffer.allocateDirect(srcBuffers[i].capacity())
.put((ByteBuffer) srcBuffers[i]).rewind();
else if (srcBuffers[0] instanceof ShortBuffer) {
dataSize = Short.SIZE >> 3; // dataSize is 2
for (i = 0; i < srcBuffers.length; i++)
clonedBuffers[i] = ByteBuffer.allocateDirect(srcBuffers[i].capacity() * dataSize)
.order(ByteOrder.nativeOrder()).asShortBuffer().put((ShortBuffer) srcBuffers[i]).rewind();
if (srcBuffers[0] instanceof ByteBuffer) {
BytePointer pointer = new BytePointer(totalCapacity);
for (int i = 0; i < srcBuffers.length; i++) {
clonedBuffers[i] = pointer.limit(pointer.position() + srcBuffers[i].limit())
.asBuffer().put((ByteBuffer)srcBuffers[i]);
pointer.position(pointer.limit());
}
opaque = pointer;
} else if (srcBuffers[0] instanceof ShortBuffer) {
ShortPointer pointer = new ShortPointer(totalCapacity);
for (int i = 0; i < srcBuffers.length; i++) {
clonedBuffers[i] = pointer.limit(pointer.position() + srcBuffers[i].limit())
.asBuffer().put((ShortBuffer)srcBuffers[i]);
pointer.position(pointer.limit());
}
opaque = pointer;
} else if (srcBuffers[0] instanceof IntBuffer) {
dataSize = Integer.SIZE >> 3; // dataSize is 4
for (i = 0; i < srcBuffers.length; i++)
clonedBuffers[i] = ByteBuffer.allocateDirect(srcBuffers[i].capacity() * dataSize)
.order(ByteOrder.nativeOrder()).asIntBuffer().put((IntBuffer) srcBuffers[i]).rewind();
IntPointer pointer = new IntPointer(totalCapacity);
for (int i = 0; i < srcBuffers.length; i++) {
clonedBuffers[i] = pointer.limit(pointer.position() + srcBuffers[i].limit())
.asBuffer().put((IntBuffer)srcBuffers[i]);
pointer.position(pointer.limit());
}
opaque = pointer;
} else if (srcBuffers[0] instanceof LongBuffer) {
dataSize = Long.SIZE >> 3; // dataSize is 8
for (i = 0; i < srcBuffers.length; i++)
clonedBuffers[i] = ByteBuffer.allocateDirect(srcBuffers[i].capacity() * dataSize)
.order(ByteOrder.nativeOrder()).asLongBuffer().put((LongBuffer) srcBuffers[i]).rewind();
LongPointer pointer = new LongPointer(totalCapacity);
for (int i = 0; i < srcBuffers.length; i++) {
clonedBuffers[i] = pointer.limit(pointer.position() + srcBuffers[i].limit())
.asBuffer().put((LongBuffer)srcBuffers[i]);
pointer.position(pointer.limit());
}
opaque = pointer;
} else if (srcBuffers[0] instanceof FloatBuffer) {
dataSize = Float.SIZE >> 3; // dataSize is 4
for (i = 0; i < srcBuffers.length; i++)
clonedBuffers[i] = ByteBuffer.allocateDirect(srcBuffers[i].capacity() * dataSize)
.order(ByteOrder.nativeOrder()).asFloatBuffer().put((FloatBuffer) srcBuffers[i]).rewind();
FloatPointer pointer = new FloatPointer(totalCapacity);
for (int i = 0; i < srcBuffers.length; i++) {
clonedBuffers[i] = pointer.limit(pointer.position() + srcBuffers[i].limit())
.asBuffer().put((FloatBuffer)srcBuffers[i]);
pointer.position(pointer.limit());
}
opaque = pointer;
} else if (srcBuffers[0] instanceof DoubleBuffer) {
dataSize = Double.SIZE >> 3; // dataSize is 8
for (i = 0; i < srcBuffers.length; i++)
clonedBuffers[i] = ByteBuffer.allocateDirect(srcBuffers[i].capacity() * dataSize)
.order(ByteOrder.nativeOrder()).asDoubleBuffer().put((DoubleBuffer) srcBuffers[i]).rewind();
DoublePointer pointer = new DoublePointer(totalCapacity);
for (int i = 0; i < srcBuffers.length; i++) {
clonedBuffers[i] = pointer.limit(pointer.position() + srcBuffers[i].limit())
.asBuffer().put((DoubleBuffer)srcBuffers[i]);
pointer.position(pointer.limit());
}
opaque = pointer;
}

for (i = 0; i < srcBuffers.length; i++)
for (int i = 0; i < srcBuffers.length; i++) {
srcBuffers[i].rewind();

clonedBuffers[i].rewind();
}
}

return clonedBuffers;

return opaque;
}

/** Returns types of data containing in the frame */
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018 Samuel Audet
* Copyright (C) 2018-2019 Samuel Audet
*
* Licensed either under the Apache License, Version 2.0, or (at your option)
* under the terms of the GNU General Public License as published by
@@ -25,6 +25,7 @@
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import org.bytedeco.javacpp.BytePointer;
import org.bytedeco.javacpp.IntPointer;
import org.bytedeco.javacpp.Loader;
import org.bytedeco.javacpp.Pointer;
@@ -62,8 +63,8 @@ public PIX convert(Frame frame) {
} else if (!isEqual(frame, pix)) {
Pointer data;
if (ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)) {
pixBuffer = ByteBuffer.allocateDirect(frame.imageHeight * frame.imageStride).order(ByteOrder.BIG_ENDIAN);
data = new Pointer(pixBuffer);
data = new BytePointer(frame.imageHeight * frame.imageStride);
pixBuffer = data.asByteBuffer().order(ByteOrder.BIG_ENDIAN);
} else {
data = new Pointer(frame.image[0].position(0));
}
@@ -110,9 +111,12 @@ public Frame convert(PIX pix) {
frame.imageChannels = pix.d() / 8;
frame.imageStride = pix.wpl() * 4;
if (ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)) {
frameBuffer = ByteBuffer.allocateDirect(frame.imageHeight * frame.imageStride).order(ByteOrder.LITTLE_ENDIAN);
Pointer data = new BytePointer(frame.imageHeight * frame.imageStride);
frameBuffer = data.asByteBuffer().order(ByteOrder.LITTLE_ENDIAN);
frame.opaque = data;
frame.image = new Buffer[] { frameBuffer };
} else {
frame.opaque = tempPix != null ? pix.clone() : pix;
frame.image = new Buffer[] { pix.createBuffer() };
}
}
@@ -123,10 +127,7 @@ public Frame convert(PIX pix) {
}

if (tempPix != null) {
frame.opaque = pix.clone();
pixDestroy(tempPix);
} else {
frame.opaque = pix;
}
return frame;
}

0 comments on commit 924f1d9

Please sign in to comment.
You can’t perform that action at this time.