Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public String start() {

// Initialize input reader
String filepath = mTest.getInput().getFilepath();
if (filepath.equals("fake_input")) {
if (filepath.startsWith("fake_input")) {
mFakeInputReader = new FakeInputReader();
if (!mFakeInputReader.openFile(filepath, inputFmt, width, height)) {
return "Could not initialize fake input";
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/com/facebook/encapp/BufferEncoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public String start() {
PixFmt inputFmt = mTest.getInput().getPixFmt();
mRefFramesizeInBytes = MediaCodecInfoHelper.frameSizeInBytes(inputFmt, sourceResolution.getWidth(), sourceResolution.getHeight());

if (mTest.getInput().getFilepath().equals("fake_input")) {
if (mTest.getInput().getFilepath().startsWith("fake_input")) {
mIsFakeInput = true;
Log.d(TAG, "Using fake input for performance testing");
mFakeInputReader = new FakeInputReader();
Expand Down
66 changes: 51 additions & 15 deletions app/src/main/java/com/facebook/encapp/CustomEncoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ class CustomEncoder extends Encoder {
public static native byte[] getHeader();
// TODO: can the size, color and bitdepth change runtime?
public static native int encode(byte[] input, byte[] output, FrameInfo info);
// Flush buffered frames. Call until returns 0.
public static native int flushEncoder(byte[] output, FrameInfo info);
// Get number of frames currently buffered in encoder
public static native int getDelayedFrames();

public static native StringParameter[] getAllEncoderSettings();

Expand Down Expand Up @@ -90,7 +94,7 @@ public CustomEncoder(Test test, String filesDir) {
Log.e(TAG, "Failed to load library, " + name + ", " + targetPath + ": " + e.getMessage());
}
}


public static byte[] readYUVFromFile(String filePath, int size, int framePosition) throws IOException {
byte[] inputBuffer = new byte[size];
Expand Down Expand Up @@ -172,7 +176,7 @@ public void setRuntimeParameters(int frame) {
}

for (Long sync : mRuntimeParams.getRequestSyncList()) {
if (sync == frame) {
if (sync == frame) {
addEncoderParameters(params, DataValueType.longType.name(), "request-sync", "");
break;
}
Expand Down Expand Up @@ -318,11 +322,13 @@ public String start() {
outputBufferSize = encode(yuvData, outputBuffer, info);
// Look at nal type as well, not just key frame?
// To ms?
mStats.stopEncodingFrame(info.getPts() , info.getSize(), info.isIFrame());
if (outputBufferSize == 0) {
return "Failed to encode frame";
} else if (outputBufferSize == -1) {
return "Encoder not started";
if (outputBufferSize < 0) {
return "Encoder not started or error occurred";
}
// outputBufferSize == 0 means frame was buffered (B-frame reordering)
// This is normal when B-frames are enabled, we'll get output later
if (outputBufferSize > 0) {
mStats.stopEncodingFrame(info.getPts() , info.getSize(), info.isIFrame());
}
currentFramePosition += frameSize;
mFramesAdded++;
Expand Down Expand Up @@ -359,16 +365,17 @@ public String start() {
muxerStarted = true;
}

ByteBuffer buffer = ByteBuffer.wrap(outputBuffer);
bufferInfo.offset = 0;
bufferInfo.size = outputBufferSize;
bufferInfo.presentationTimeUs = info.getPts();
// Only write to muxer if we have actual output
if (outputBufferSize > 0 && mMuxerWrapper != null) {
ByteBuffer buffer = ByteBuffer.wrap(outputBuffer);
bufferInfo.offset = 0;
bufferInfo.size = outputBufferSize;
bufferInfo.presentationTimeUs = info.getPts();

//TODO: we get this from FrameInfo instead
boolean isKeyFrame = checkIfKeyFrame(outputBuffer);
if (isKeyFrame) bufferInfo.flags = MediaCodec.BUFFER_FLAG_KEY_FRAME;
boolean isKeyFrame = checkIfKeyFrame(outputBuffer);
if (isKeyFrame) bufferInfo.flags = MediaCodec.BUFFER_FLAG_KEY_FRAME;
else bufferInfo.flags = 0;

if(mMuxerWrapper != null) {
buffer.position(bufferInfo.offset);
buffer.limit(bufferInfo.offset + bufferInfo.size);

Expand All @@ -382,6 +389,35 @@ public String start() {
}
mStats.stop();

// Flush any remaining buffered frames (important for B-frames/multi-threading)
int delayedFrames = getDelayedFrames();
Log.d(TAG, "Flushing " + delayedFrames + " delayed frames from encoder");
while (delayedFrames > 0) {
info = new FrameInfo(0);
outputBufferSize = flushEncoder(outputBuffer, info);
if (outputBufferSize <= 0) {
break; // No more frames or error
}
Log.d(TAG, "Flushed frame: pts=" + info.getPts() + ", size=" + outputBufferSize);

// Write flushed frame to muxer
if (mMuxerWrapper != null && muxerStarted) {
ByteBuffer buffer = ByteBuffer.wrap(outputBuffer);
bufferInfo.offset = 0;
bufferInfo.size = outputBufferSize;
bufferInfo.presentationTimeUs = info.getPts();

boolean isKeyFrame = checkIfKeyFrame(outputBuffer);
bufferInfo.flags = isKeyFrame ? MediaCodec.BUFFER_FLAG_KEY_FRAME : 0;

buffer.position(bufferInfo.offset);
buffer.limit(bufferInfo.offset + bufferInfo.size);
mMuxerWrapper.writeSampleData(videoTrackIndex, buffer, bufferInfo);
}
delayedFrames = getDelayedFrames();
}
Log.d(TAG, "Encoder flush complete");

Log.d(TAG, "Close encoder and streams");
close();

Expand Down
51 changes: 47 additions & 4 deletions app/src/main/java/com/facebook/encapp/SurfaceEncoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class SurfaceEncoder extends Encoder implements VsyncListener {
boolean mIsCameraSource = false;
boolean mIsFakeInput = false;
FakeGLRenderer mFakeGLRenderer; // GL-based fake input (fast!)
FakeGLRenderer.PatternType mFakeInputPatternType = FakeGLRenderer.PatternType.TEXTURE; // Default pattern
boolean mUseCameraTimestamp = true;
OutputMultiplier mOutputMult;
Bundle mKeyFrameBundle;
Expand Down Expand Up @@ -115,10 +116,12 @@ public String encode(
if (mTest.getInput().getPixFmt().getNumber() == PixFmt.rgba_VALUE) {
mIsRgbaSource = true;
mRefFramesizeInBytes = width * height * 4;
} else if (mTest.getInput().getFilepath().equals("fake_input")) {
} else if (mTest.getInput().getFilepath().startsWith("fake_input")) {
mIsFakeInput = true;
// Parse pattern type from "fake_input.type" notation
mFakeInputPatternType = parseFakeInputPatternType(mTest.getInput().getFilepath());
// Use GL rendering for fake input - ZERO CPU overhead!
Log.d(TAG, "Using fake input with GL rendering for performance testing");
Log.d(TAG, "Using fake input with GL rendering, pattern: " + mFakeInputPatternType);
} else if (mTest.getInput().getFilepath().equals("camera")) {
mIsCameraSource = true;
//TODO: handle other fps (i.e. try to set lower or higher fps)
Expand Down Expand Up @@ -164,8 +167,9 @@ public String encode(
if (mIsFakeInput) {
// Initialize FakeGLRenderer (will be set up later on GL thread)
mFakeGLRenderer = new FakeGLRenderer();
mFakeGLRenderer.setPatternType(FakeGLRenderer.PatternType.TEXTURE);
Log.d(TAG, "Created FakeGLRenderer for GL-based fake input");
mFakeGLRenderer.setPatternType(mFakeInputPatternType);
mFakeGLRenderer.setDimensions(width, height);
Log.d(TAG, "Created FakeGLRenderer for GL-based fake input with pattern: " + mFakeInputPatternType);
// Initialize on GL thread after OutputMultiplier is ready
} else {
mYuvReader = new FileReader();
Expand Down Expand Up @@ -714,4 +718,43 @@ public void vsync(long frameTimeNs) {
mSyncLock.notifyAll();
}
}

/**
* Parse pattern type from fake_input filepath notation.
* Supports: "fake_input", "fake_input.clock", "fake_input.texture", "fake_input.gradient", "fake_input.solid"
*
* @param filepath The input filepath (e.g., "fake_input.clock")
* @return The parsed PatternType, defaults to TEXTURE if not specified or unknown
*/
private FakeGLRenderer.PatternType parseFakeInputPatternType(String filepath) {
if (filepath == null || !filepath.startsWith("fake_input")) {
return FakeGLRenderer.PatternType.TEXTURE;
}

// Check for ".type" suffix
if (filepath.contains(".")) {
String suffix = filepath.substring(filepath.lastIndexOf('.') + 1).toLowerCase();
switch (suffix) {
case "clock":
Log.d(TAG, "Parsed fake_input pattern type: CLOCK");
return FakeGLRenderer.PatternType.CLOCK;
case "texture":
Log.d(TAG, "Parsed fake_input pattern type: TEXTURE");
return FakeGLRenderer.PatternType.TEXTURE;
case "gradient":
Log.d(TAG, "Parsed fake_input pattern type: GRADIENT");
return FakeGLRenderer.PatternType.GRADIENT;
case "solid":
Log.d(TAG, "Parsed fake_input pattern type: SOLID");
return FakeGLRenderer.PatternType.SOLID;
default:
Log.w(TAG, "Unknown fake_input pattern type: " + suffix + ", using TEXTURE");
return FakeGLRenderer.PatternType.TEXTURE;
}
}

// No suffix, use default
Log.d(TAG, "No pattern type specified, using default: TEXTURE");
return FakeGLRenderer.PatternType.TEXTURE;
}
}
Loading