Skip to content

Commit 05d0c5b

Browse files
committed
feat: add CustomVideoSource and related JNI bindings for video frame handling
1 parent 3907bf1 commit 05d0c5b

File tree

9 files changed

+387
-6
lines changed

9 files changed

+387
-6
lines changed

webrtc-jni/src/main/cpp/include/JNI_CustomAudioSource.h

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

webrtc-jni/src/main/cpp/include/JNI_CustomVideoSource.h

Lines changed: 37 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

webrtc-jni/src/main/cpp/src/JNI_CustomAudioSource.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
#include "media/audio/CustomAudioSource.h"
2121

22+
#include "rtc_base/logging.h"
23+
2224
JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_CustomAudioSource_initialize
2325
(JNIEnv * env, jobject caller)
2426
{
@@ -28,6 +30,23 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_CustomAudioSource_init
2830
SetHandle(env, caller, source.release());
2931
}
3032

33+
JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_CustomAudioSource_dispose
34+
(JNIEnv * env, jobject caller)
35+
{
36+
jni::CustomAudioSource * source = GetHandle<jni::CustomAudioSource>(env, caller);
37+
CHECK_HANDLE(source);
38+
39+
webrtc::RefCountReleaseStatus status = source->Release();
40+
41+
if (status != webrtc::RefCountReleaseStatus::kDroppedLastRef) {
42+
RTC_LOG(LS_WARNING) << "Native object was not deleted. A reference is still around somewhere.";
43+
}
44+
45+
SetHandle<std::nullptr_t>(env, caller, nullptr);
46+
47+
source = nullptr;
48+
}
49+
3150
JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_CustomAudioSource_pushAudio
3251
(JNIEnv * env, jobject caller, jbyteArray audioData, jint bits_per_sample, jint sampleRate, jint channels, jint frameCount)
3352
{
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2019 Alex Andres
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "JNI_CustomVideoSource.h"
18+
#include "JavaUtils.h"
19+
#include "api/VideoFrame.h"
20+
21+
#include "media/video/CustomVideoSource.h"
22+
23+
#include "rtc_base/logging.h"
24+
25+
JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_video_CustomVideoSource_initialize
26+
(JNIEnv * env, jobject caller)
27+
{
28+
std::shared_ptr<jni::SyncClock> sync_clock = std::make_shared<jni::SyncClock>();
29+
webrtc::scoped_refptr<jni::CustomVideoSource> source = webrtc::make_ref_counted<jni::CustomVideoSource>(sync_clock);
30+
31+
SetHandle(env, caller, source.release());
32+
}
33+
34+
JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_video_CustomVideoSource_dispose
35+
(JNIEnv * env, jobject caller)
36+
{
37+
jni::CustomVideoSource * source = GetHandle<jni::CustomVideoSource>(env, caller);
38+
CHECK_HANDLE(source);
39+
40+
webrtc::RefCountReleaseStatus status = source->Release();
41+
42+
if (status != webrtc::RefCountReleaseStatus::kDroppedLastRef) {
43+
RTC_LOG(LS_WARNING) << "Native object was not deleted. A reference is still around somewhere.";
44+
}
45+
46+
SetHandle<std::nullptr_t>(env, caller, nullptr);
47+
48+
source = nullptr;
49+
}
50+
51+
JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_video_CustomVideoSource_pushFrame
52+
(JNIEnv * env, jobject caller, jobject javaFrame)
53+
{
54+
jni::CustomVideoSource * source = GetHandle<jni::CustomVideoSource>(env, caller);
55+
CHECK_HANDLE(source);
56+
57+
if (javaFrame != nullptr) {
58+
auto frame = jni::JavaLocalRef<jobject>(env, javaFrame);
59+
webrtc::VideoFrame nativeFrame = jni::VideoFrame::toNative(env, frame);
60+
61+
source->PushFrame(nativeFrame);
62+
}
63+
}

webrtc-jni/src/main/cpp/src/api/VideoFrame.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "JavaUtils.h"
2020
#include "JNI_WebRTC.h"
2121

22+
#include "api/video/i420_buffer.h"
2223
#include "rtc_base/time_utils.h"
2324

2425
namespace jni
@@ -32,9 +33,39 @@ namespace jni
3233

3334
int rotation = obj.getInt(javaClass->rotation);
3435
int64_t timestamp_ns = obj.getLong(javaClass->timestampNs);
36+
JavaLocalRef<jobject> jBuffer = obj.getObject(javaClass->buffer);
37+
38+
/*
39+
// Get the I420Buffer properties
40+
const auto i420Class = JavaClasses::get<JavaNativeI420BufferClass>(env);
41+
JavaObject bufferObj(env, jBuffer);
42+
43+
int width = bufferObj.getInt(i420Class->width);
44+
int height = bufferObj.getInt(i420Class->height);
45+
46+
JavaLocalRef<jobject> dataY = bufferObj.getObject(i420Class->dataY);
47+
JavaLocalRef<jobject> dataU = bufferObj.getObject(i420Class->dataU);
48+
JavaLocalRef<jobject> dataV = bufferObj.getObject(i420Class->dataV);
49+
50+
int strideY = bufferObj.getInt(i420Class->strideY);
51+
int strideU = bufferObj.getInt(i420Class->strideU);
52+
int strideV = bufferObj.getInt(i420Class->strideV);
53+
54+
// Get the direct buffer addresses
55+
const uint8_t * y_data = static_cast<uint8_t*>(env->GetDirectBufferAddress(dataY.get()));
56+
const uint8_t * u_data = static_cast<uint8_t*>(env->GetDirectBufferAddress(dataU.get()));
57+
const uint8_t * v_data = static_cast<uint8_t*>(env->GetDirectBufferAddress(dataV.get()));
58+
59+
// Create a native I420Buffer
60+
webrtc::scoped_refptr<webrtc::I420Buffer> buffer = webrtc::I420Buffer::Copy(
61+
width, height, y_data, strideY, u_data, strideU, v_data, strideV);
62+
*/
63+
64+
webrtc::I420BufferInterface * i420Buffer = GetHandle<webrtc::I420BufferInterface>(env, jBuffer);
65+
webrtc::scoped_refptr<webrtc::I420BufferInterface> buffer(i420Buffer);
3566

3667
return webrtc::VideoFrame::Builder()
37-
//.set_video_frame_buffer(buffer)
68+
.set_video_frame_buffer(buffer)
3869
//.set_timestamp_rtp(timestamp_rtp)
3970
.set_timestamp_ms(timestamp_ns / webrtc::kNumNanosecsPerMillisec)
4071
.set_rotation(static_cast<webrtc::VideoRotation>(rotation))

webrtc/src/main/java/dev/onvoid/webrtc/media/audio/CustomAudioSource.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ public CustomAudioSource() {
4545
public native void pushAudio(byte[] audioData, int bits_per_sample,
4646
int sampleRate, int channels, int frameCount);
4747

48+
/**
49+
* Disposes of any native resources held by this audio source.
50+
* This method should be called when the audio source is no longer needed
51+
* to prevent memory leaks.
52+
*/
53+
public native void dispose();
54+
4855
/**
4956
* Initializes the native resources required by this audio source.
5057
*/
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2019 Alex Andres
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package dev.onvoid.webrtc.media.video;
18+
19+
/**
20+
* Custom implementation of a video source for WebRTC that allows pushing video frames
21+
* from external sources directly to the WebRTC video pipeline.
22+
*
23+
* @author Alex Andres
24+
*/
25+
public class CustomVideoSource extends VideoTrackSource {
26+
27+
/**
28+
* Constructs a new CustomVideoSource instance.
29+
*/
30+
public CustomVideoSource() {
31+
super();
32+
33+
initialize();
34+
}
35+
36+
/**
37+
* Pushes audio data to be processed by this audio source.
38+
*
39+
* @param frame The video frame to be pushed to the source.
40+
*/
41+
public native void pushFrame(VideoFrame frame);
42+
43+
/**
44+
* Disposes of any native resources held by this video source.
45+
* This method should be called when the video source is no longer needed
46+
* to prevent memory leaks.
47+
*/
48+
public native void dispose();
49+
50+
/**
51+
* Initializes the native resources required by this video source.
52+
*/
53+
private native void initialize();
54+
55+
}

webrtc/src/main/java/dev/onvoid/webrtc/media/video/VideoFrame.java

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,16 @@
1919
import dev.onvoid.webrtc.internal.RefCounted;
2020

2121
/**
22-
* Represents a video frame with an associated frame buffer and metadata.
23-
* This class implements reference counting to manage memory for video frames.
24-
* Video frames contain buffer data along with rotation and timestamp information.
22+
* Represents a video frame with an underlying buffer, rotation information, and timestamp.
23+
* <p>
24+
* A VideoFrame holds reference to its buffer data and implements the RefCounted
25+
* interface to manage the lifecycle of native resources through reference counting.
26+
* The frame includes a timestamp in nanoseconds for synchronization purposes.
27+
*
28+
* @author Alex Andres
2529
*/
2630
public class VideoFrame implements RefCounted {
27-
31+
2832
/** The underlying frame buffer. */
2933
public final VideoFrameBuffer buffer;
3034

@@ -35,7 +39,28 @@ public class VideoFrame implements RefCounted {
3539
public final long timestampNs;
3640

3741

38-
private VideoFrame(VideoFrameBuffer buffer, int rotation, long timestampNs) {
42+
/**
43+
* Creates a new VideoFrame with the specified buffer, rotation and timestamp.
44+
*
45+
* @param buffer The video frame buffer containing the actual frame data.
46+
* @param timestampNs The timestamp of the frame in nanoseconds.
47+
*
48+
* @throws IllegalArgumentException If buffer is null.
49+
*/
50+
public VideoFrame(VideoFrameBuffer buffer, long timestampNs) {
51+
this(buffer, 0, timestampNs);
52+
}
53+
54+
/**
55+
* Creates a new VideoFrame with the specified buffer, rotation and timestamp.
56+
*
57+
* @param buffer The video frame buffer containing the actual frame data.
58+
* @param rotation The rotation of the frame in degrees (must be a multiple of 90).
59+
* @param timestampNs The timestamp of the frame in nanoseconds.
60+
*
61+
* @throws IllegalArgumentException If buffer is null or rotation is not a multiple of 90.
62+
*/
63+
public VideoFrame(VideoFrameBuffer buffer, int rotation, long timestampNs) {
3964
if (buffer == null) {
4065
throw new IllegalArgumentException("VideoFrameBuffer must not be null");
4166
}

0 commit comments

Comments
 (0)