Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow stream and record with differents resolutions #1634

Merged
merged 4 commits into from
Nov 14, 2024
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 @@ -181,8 +181,8 @@ class CameraFragment: Fragment(), ConnectChecker {

private fun prepare() {
val prepared = try {
genericStream.prepareVideo(width, height, vBitrate, rotation = rotation) &&
genericStream.prepareAudio(sampleRate, isStereo, aBitrate)
genericStream.prepareVideo(width, height, vBitrate, rotation = rotation)
&& genericStream.prepareAudio(sampleRate, isStereo, aBitrate)
} catch (e: IllegalArgumentException) {
false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,22 +93,19 @@ class VideoFileSource(

@Throws(IOException::class)
fun replaceFile(context: Context, uri: Uri) {
val width = videoDecoder.width
val height = videoDecoder.height
val wasRunning = videoDecoder.isRunning
val videoDecoder = VideoDecoder(videoDecoderInterface, decoderInterface)
videoDecoder.extractor = this.videoDecoder.extractor
if (!videoDecoder.initExtractor(context, uri)) throw IOException("Extraction failed")
if (width != videoDecoder.width || height != videoDecoder.height) throw IOException("Resolution must be the same that the previous file")
this.videoDecoder.stop()
this.videoDecoder = videoDecoder
if (wasRunning) {
videoDecoder.prepareVideo(Surface(surfaceTexture))
videoDecoder.start()
}
}

fun setExtractor(extractor: Extractor) {
videoDecoder.extractor = extractor
}
fun setExtractor(extractor: Extractor) {
videoDecoder.extractor = extractor
}
}
97 changes: 86 additions & 11 deletions library/src/main/java/com/pedro/library/base/Camera2Base.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,12 @@ public abstract class Camera2Base {
private final Context context;
private Camera2ApiManager cameraManager;
protected VideoEncoder videoEncoder;
protected VideoEncoder videoEncoderRecord;
private MicrophoneManager microphoneManager;
private AudioEncoder audioEncoder;
private boolean streaming = false;
private GlInterface glInterface;
private boolean differentRecordResolution = false;
protected boolean audioInitialized = false;
private boolean onPreview = false;
private boolean isBackground = false;
Expand All @@ -111,6 +113,7 @@ private void init(Context context) {
cameraManager = new Camera2ApiManager(context);
microphoneManager = new MicrophoneManager(getMicrophoneData);
videoEncoder = new VideoEncoder(getVideoData);
videoEncoderRecord = new VideoEncoder(getVideoDataRecord);
audioEncoder = new AudioEncoder(getAudioData);
recordController = new AndroidMuxerRecordController();
}
Expand All @@ -125,6 +128,7 @@ public void setCameraCallbacks(CameraCallbacks callbacks) {
*/
public void setTimestampMode(TimestampMode timestampModeVideo, TimestampMode timestampModeAudio) {
videoEncoder.setTimestampMode(timestampModeVideo);
videoEncoderRecord.setTimestampMode(timestampModeVideo);
audioEncoder.setTimestampMode(timestampModeAudio);
}

Expand All @@ -134,6 +138,7 @@ public void setTimestampMode(TimestampMode timestampModeVideo, TimestampMode tim
*/
public void setEncoderErrorCallback(EncoderErrorCallback encoderErrorCallback) {
videoEncoder.setEncoderErrorCallback(encoderErrorCallback);
videoEncoderRecord.setEncoderErrorCallback(encoderErrorCallback);
audioEncoder.setEncoderErrorCallback(encoderErrorCallback);
}

Expand Down Expand Up @@ -249,6 +254,12 @@ public String getCurrentCameraId() {
}

public boolean resetVideoEncoder() {
if (differentRecordResolution) {
glInterface.removeMediaCodecRecordSurface();
boolean result = videoEncoderRecord.reset();
if (!result) return false;
glInterface.addMediaCodecRecordSurface(videoEncoderRecord.getInputSurface());
}
glInterface.removeMediaCodecSurface();
boolean result = videoEncoder.reset();
if (!result) return false;
Expand All @@ -275,16 +286,39 @@ public boolean resetAudioEncoder() {
* @return true if success, false if you get a error (Normally because the encoder selected
* doesn't support any configuration seated or your device hasn't a H264 encoder).
*/
public boolean prepareVideo(int width, int height, int fps, int bitrate, int iFrameInterval,
int rotation, int profile, int level) {
public boolean prepareVideo(
int width, int height, int fps, int bitrate, int iFrameInterval,
int rotation, int profile, int level,
int recordWidth, int recordHeight, int recordBitrate
) {
if (onPreview && (width != previewWidth || height != previewHeight
|| fps != videoEncoder.getFps() || rotation != videoEncoder.getRotation())) {
stopPreview();
}
differentRecordResolution = false;
if (recordWidth != width && recordHeight != height) {
if ((double) recordWidth / (double) recordHeight != (double) width / (double) height) {
Log.e(TAG, "The aspect ratio of record and stream resolution must be the same");
return false;
}
differentRecordResolution = true;
}
if (differentRecordResolution) {
boolean result = videoEncoderRecord.prepareVideoEncoder(recordWidth, recordHeight, fps, recordBitrate, rotation,
iFrameInterval, FormatVideoEncoder.SURFACE, profile, level);
if (!result) return false;
}
return videoEncoder.prepareVideoEncoder(width, height, fps, bitrate, rotation,
iFrameInterval, FormatVideoEncoder.SURFACE, profile, level);
}

public boolean prepareVideo(
int width, int height, int fps, int bitrate, int iFrameInterval,
int rotation, int profile, int level
) {
return prepareVideo(width, height, fps, bitrate, iFrameInterval, rotation, profile, level, width, height, bitrate);
}

public boolean prepareVideo(int width, int height, int fps, int bitrate, int iFrameInterval,
int rotation) {
return prepareVideo(width, height, fps, bitrate, iFrameInterval, rotation, -1, -1);
Expand Down Expand Up @@ -364,6 +398,7 @@ public boolean prepareAudio() {
*/
public void forceCodecType(CodecUtil.CodecType codecTypeVideo, CodecUtil.CodecType codecTypeAudio) {
videoEncoder.forceCodecType(codecTypeVideo);
videoEncoderRecord.forceCodecType(codecTypeVideo);
audioEncoder.forceCodecType(codecTypeAudio);
}

Expand All @@ -378,7 +413,7 @@ public void startRecord(@NonNull String path, @Nullable RecordController.Listene
recordController.startRecord(path, listener);
if (!streaming) {
startEncoders();
} else if (videoEncoder.isRunning()) {
} else if (videoEncoder.isRunning() || videoEncoderRecord.isRunning()) {
requestKeyFrame();
}
}
Expand All @@ -399,7 +434,7 @@ public void startRecord(@NonNull final FileDescriptor fd,
recordController.startRecord(fd, listener);
if (!streaming) {
startEncoders();
} else if (videoEncoder.isRunning()) {
} else if (videoEncoder.isRunning() || videoEncoderRecord.isRunning()) {
requestKeyFrame();
}
}
Expand Down Expand Up @@ -434,18 +469,24 @@ public void replaceView(OpenGlView openGlView) {
private void replaceGlInterface(GlInterface glInterface) {
if (isStreaming() || isRecording() || isOnPreview()) {
Point size = this.glInterface.getEncoderSize();
Point sizeRecord = this.glInterface.getEncoderSize();
cameraManager.closeCamera();
this.glInterface.removeMediaCodecSurface();
this.glInterface.removeMediaCodecRecordSurface();
this.glInterface.stop();
this.glInterface = glInterface;
int w = size.x;
int h = size.y;
int recordW = sizeRecord.x;
int recordH = sizeRecord.y;
int rotation = videoEncoder.getRotation();
if (rotation == 90 || rotation == 270) {
h = size.x;
w = size.y;
recordH = sizeRecord.x;
recordW = sizeRecord.y;
}
prepareGlView(w, h, rotation);
prepareGlView(w, h, recordW, recordH, rotation);
cameraManager.openLastCamera();
} else {
this.glInterface = glInterface;
Expand Down Expand Up @@ -478,7 +519,9 @@ public void startPreview(String cameraId, int width, int height, int fps, int ro
previewHeight = height;
videoEncoder.setFps(fps);
videoEncoder.setRotation(rotation);
prepareGlView(width, height, rotation);
videoEncoderRecord.setFps(fps);
videoEncoderRecord.setRotation(rotation);
prepareGlView(width, height, width, height, rotation);
cameraManager.openCameraId(cameraId);
onPreview = true;
} else if (!isStreaming() && !onPreview && isBackground) {
Expand Down Expand Up @@ -588,8 +631,9 @@ public void startStream(String url) {
private void startEncoders() {
long startTs = System.nanoTime() / 1000;
videoEncoder.start(startTs);
if (differentRecordResolution) videoEncoderRecord.start(startTs);
if (audioInitialized) audioEncoder.start(startTs);
prepareGlView(videoEncoder.getWidth(), videoEncoder.getHeight(), videoEncoder.getRotation());
prepareGlView(videoEncoder.getWidth(), videoEncoder.getHeight(), videoEncoderRecord.getWidth(), videoEncoderRecord.getHeight(), videoEncoder.getRotation());
if (audioInitialized) microphoneManager.start();
if (!cameraManager.isRunning()) cameraManager.openLastCamera();
onPreview = true;
Expand All @@ -599,18 +643,26 @@ public void requestKeyFrame() {
if (videoEncoder.isRunning()) {
videoEncoder.requestKeyframe();
}
if (videoEncoderRecord.isRunning()) {
videoEncoderRecord.requestKeyframe();
}
}

private void prepareGlView(int width, int height, int rotation) {
private void prepareGlView(int width, int height, int recordWidth, int recordHeight, int rotation) {
int w = width;
int h = height;
int recordW = recordWidth;
int recordH = recordHeight;
boolean isPortrait = false;
if (rotation == 90 || rotation == 270) {
h = width;
w = height;
recordH = recordWidth;
recordW = recordHeight;
isPortrait = true;
}
glInterface.setEncoderSize(w, h);
if (differentRecordResolution) glInterface.setEncoderRecordSize(recordW, recordH);
if (glInterface instanceof GlStreamInterface glStreamInterface) {
glStreamInterface.setPreviewResolution(w, h);
glStreamInterface.setIsPortrait(isPortrait);
Expand All @@ -620,8 +672,12 @@ private void prepareGlView(int width, int height, int rotation) {
if (videoEncoder.getInputSurface() != null && videoEncoder.isRunning()) {
glInterface.addMediaCodecSurface(videoEncoder.getInputSurface());
}
cameraManager.prepareCamera(glInterface.getSurfaceTexture(), videoEncoder.getWidth(),
videoEncoder.getHeight(), videoEncoder.getFps());
if (videoEncoderRecord.getInputSurface() != null && videoEncoderRecord.isRunning()) {
glInterface.addMediaCodecRecordSurface(videoEncoderRecord.getInputSurface());
}
int cameraWidth = Math.max(videoEncoder.getWidth(), videoEncoderRecord.getWidth());
int cameraHeight = Math.max(videoEncoder.getHeight(), videoEncoderRecord.getHeight());
cameraManager.prepareCamera(glInterface.getSurfaceTexture(), cameraWidth, cameraHeight, videoEncoder.getFps());
}

protected abstract void stopStreamImp();
Expand All @@ -638,11 +694,13 @@ public void stopStream() {
onPreview = !isBackground;
if (audioInitialized) microphoneManager.stop();
glInterface.removeMediaCodecSurface();
glInterface.removeMediaCodecRecordSurface();
if (glInterface instanceof GlStreamInterface) {
glInterface.stop();
cameraManager.closeCamera();
}
videoEncoder.stop();
if (differentRecordResolution) videoEncoderRecord.stop();
if (audioInitialized) audioEncoder.stop();
recordController.resetFormats();
}
Expand Down Expand Up @@ -864,6 +922,7 @@ public void setVideoBitrateOnFly(int bitrate) {
public void forceFpsLimit(boolean enabled) {
int fps = enabled ? videoEncoder.getFps() : 0;
videoEncoder.setForceFps(fps);
videoEncoderRecord.setForceFps(fps);
glInterface.forceFpsLimit(fps);
}

Expand Down Expand Up @@ -961,10 +1020,26 @@ public void onVideoInfo(@NonNull ByteBuffer sps, @Nullable ByteBuffer pps, @Null
@Override
public void getVideoData(@NonNull ByteBuffer videoBuffer, @NonNull MediaCodec.BufferInfo info) {
fpsListener.calculateFps();
recordController.recordVideo(videoBuffer, info);
if (!differentRecordResolution) recordController.recordVideo(videoBuffer, info);
if (streaming) getVideoDataImp(videoBuffer, info);
}

@Override
public void onVideoFormat(@NonNull MediaFormat mediaFormat) {
if (!differentRecordResolution) recordController.setVideoFormat(mediaFormat, !audioInitialized);
}
};

private final GetVideoData getVideoDataRecord = new GetVideoData() {
@Override
public void onVideoInfo(@NonNull ByteBuffer sps, @Nullable ByteBuffer pps, @Nullable ByteBuffer vps) {
}

@Override
public void getVideoData(@NonNull ByteBuffer videoBuffer, @NonNull MediaCodec.BufferInfo info) {
recordController.recordVideo(videoBuffer, info);
}

@Override
public void onVideoFormat(@NonNull MediaFormat mediaFormat) {
recordController.setVideoFormat(mediaFormat, !audioInitialized);
Expand Down
Loading
Loading