Skip to content

Commit

Permalink
Android improvements.
Browse files Browse the repository at this point in the history
Start/Stop receiving stream method for VideoTrack (#25)

Properly remove observer upon deconstruction (#26)

feat: Expose setCodecPreferences/getCapabilities for android. (#61)

fix: add WrappedVideoDecoderFactory.java. (#74)

Co-authored-by: davidliu <davidliu@deviange.net>
  • Loading branch information
davidliu authored and cloudwebrtc committed May 22, 2024
1 parent b0b9fe9 commit 9aaaab5
Show file tree
Hide file tree
Showing 18 changed files with 217 additions and 3 deletions.
4 changes: 4 additions & 0 deletions api/media_stream_interface.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ const char* const MediaStreamTrackInterface::kVideoKind =
const char* const MediaStreamTrackInterface::kAudioKind =
cricket::kMediaTypeAudio;

bool VideoTrackInterface::should_receive() const {
return true;
}

VideoTrackInterface::ContentHint VideoTrackInterface::content_hint() const {
return ContentHint::kNone;
}
Expand Down
2 changes: 2 additions & 0 deletions api/media_stream_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ class RTC_EXPORT VideoTrackInterface

virtual VideoTrackSourceInterface* GetSource() const = 0;

virtual void set_should_receive(bool should_receive) {}
virtual bool should_receive() const;
virtual ContentHint content_hint() const;
virtual void set_content_hint(ContentHint hint) {}

Expand Down
2 changes: 2 additions & 0 deletions media/base/media_channel.h
Original file line number Diff line number Diff line change
Expand Up @@ -1002,6 +1002,8 @@ class VideoMediaReceiveChannelInterface : public MediaReceiveChannelInterface {
webrtc::RtcpMode rtcp_mode,
absl::optional<int> rtx_time) = 0;
virtual bool AddDefaultRecvStreamForTesting(const StreamParams& sp) = 0;
virtual void StartReceive(uint32_t ssrc) {}
virtual void StopReceive(uint32_t ssrc) {}
};

} // namespace cricket
Expand Down
18 changes: 18 additions & 0 deletions media/engine/webrtc_video_engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3500,6 +3500,24 @@ void WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::SetReceiverParameters(
}
}

void WebRtcVideoReceiveChannel::StartReceive(uint32_t ssrc) {
RTC_DCHECK_RUN_ON(&thread_checker_);
WebRtcVideoReceiveStream* stream = FindReceiveStream(ssrc);
if(!stream) {
return;
}
stream->StartReceiveStream();
}

void WebRtcVideoReceiveChannel::StopReceive(uint32_t ssrc) {
RTC_DCHECK_RUN_ON(&thread_checker_);
WebRtcVideoReceiveStream* stream = FindReceiveStream(ssrc);
if(!stream) {
return;
}
stream->StopReceiveStream();
}

void WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::
RecreateReceiveStream() {
RTC_DCHECK_RUN_ON(&thread_checker_);
Expand Down
3 changes: 2 additions & 1 deletion media/engine/webrtc_video_engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -632,7 +632,8 @@ class WebRtcVideoReceiveChannel : public MediaChannelUtil,
bool nack_enabled,
webrtc::RtcpMode rtcp_mode,
absl::optional<int> rtx_time) override;

void StartReceive(uint32_t ssrc) override;
void StopReceive(uint32_t ssrc) override;
private:
class WebRtcVideoReceiveStream;
struct ChangedReceiverParameters {
Expand Down
2 changes: 2 additions & 0 deletions pc/media_stream_track_proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ PROXY_SECONDARY_METHOD2(void,
PROXY_SECONDARY_METHOD1(void, RemoveSink, rtc::VideoSinkInterface<VideoFrame>*)
PROXY_SECONDARY_METHOD0(void, RequestRefreshFrame)
BYPASS_PROXY_CONSTMETHOD0(VideoTrackSourceInterface*, GetSource)
PROXY_CONSTMETHOD0(bool, should_receive)
PROXY_METHOD1(void, set_should_receive, bool)

PROXY_METHOD1(void, RegisterObserver, ObserverInterface*)
PROXY_METHOD1(void, UnregisterObserver, ObserverInterface*)
Expand Down
41 changes: 40 additions & 1 deletion pc/video_rtp_receiver.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,20 @@ VideoRtpReceiver::VideoRtpReceiver(
rtc::Thread::Current(),
worker_thread,
VideoTrack::Create(receiver_id, source_, worker_thread))),
attachment_id_(GenerateUniqueId()) {
cached_track_should_receive_(track_->should_receive()),
attachment_id_(GenerateUniqueId()),
worker_thread_safety_(PendingTaskSafetyFlag::CreateDetachedInactive()) {
RTC_DCHECK(worker_thread_);
SetStreams(streams);
track_->RegisterObserver(this);
RTC_DCHECK_EQ(source_->state(), MediaSourceInterface::kInitializing);
}

VideoRtpReceiver::~VideoRtpReceiver() {
RTC_DCHECK_RUN_ON(&signaling_thread_checker_);
RTC_DCHECK(!media_channel_);

track_->UnregisterObserver(this);
}

std::vector<std::string> VideoRtpReceiver::stream_ids() const {
Expand Down Expand Up @@ -114,6 +119,39 @@ void VideoRtpReceiver::Stop() {
track_->internal()->set_ended();
}

void VideoRtpReceiver::OnChanged() {
RTC_DCHECK_RUN_ON(&signaling_thread_checker_);
if (cached_track_should_receive_ != track_->should_receive()) {
cached_track_should_receive_ = track_->should_receive();
worker_thread_->PostTask(
[this, receive = cached_track_should_receive_]() {
RTC_DCHECK_RUN_ON(worker_thread_);
if(receive) {
StartMediaChannel();
} else {
StopMediaChannel();
}
});
}
}

void VideoRtpReceiver::StartMediaChannel() {
RTC_DCHECK_RUN_ON(worker_thread_);
if (!media_channel_) {
return;
}
media_channel_->StartReceive(signaled_ssrc_.value_or(0));
OnGenerateKeyFrame();
}

void VideoRtpReceiver::StopMediaChannel() {
RTC_DCHECK_RUN_ON(worker_thread_);
if (!media_channel_) {
return;
}
media_channel_->StopReceive(signaled_ssrc_.value_or(0));
}

void VideoRtpReceiver::RestartMediaChannel(absl::optional<uint32_t> ssrc) {
RTC_DCHECK_RUN_ON(&signaling_thread_checker_);
MediaSourceInterface::SourceState state = source_->state();
Expand Down Expand Up @@ -209,6 +247,7 @@ void VideoRtpReceiver::set_transport(
void VideoRtpReceiver::SetStreams(
const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams) {
RTC_DCHECK_RUN_ON(&signaling_thread_checker_);

// Remove remote track from any streams that are going away.
for (const auto& existing_stream : streams_) {
bool removed = true;
Expand Down
11 changes: 10 additions & 1 deletion pc/video_rtp_receiver.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@

namespace webrtc {

class VideoRtpReceiver : public RtpReceiverInternal {
class VideoRtpReceiver : public RtpReceiverInternal,
public ObserverInterface {
public:
// An SSRC of 0 will create a receiver that will match the first SSRC it
// sees. Must be called on signaling thread.
Expand All @@ -60,6 +61,9 @@ class VideoRtpReceiver : public RtpReceiverInternal {

rtc::scoped_refptr<VideoTrackInterface> video_track() const { return track_; }

// ObserverInterface implementation
void OnChanged() override;

// RtpReceiverInterface implementation
rtc::scoped_refptr<MediaStreamTrackInterface> track() const override {
return track_;
Expand Down Expand Up @@ -115,6 +119,8 @@ class VideoRtpReceiver : public RtpReceiverInternal {
cricket::MediaReceiveChannelInterface* media_channel);

private:
void StartMediaChannel();
void StopMediaChannel();
void RestartMediaChannel(absl::optional<uint32_t> ssrc)
RTC_RUN_ON(&signaling_thread_checker_);
void RestartMediaChannel_w(absl::optional<uint32_t> ssrc,
Expand Down Expand Up @@ -162,6 +168,8 @@ class VideoRtpReceiver : public RtpReceiverInternal {
RTC_GUARDED_BY(&signaling_thread_checker_) = nullptr;
bool received_first_packet_ RTC_GUARDED_BY(&signaling_thread_checker_) =
false;

bool cached_track_should_receive_ RTC_GUARDED_BY(&signaling_thread_checker_);
const int attachment_id_;
rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor_
RTC_GUARDED_BY(worker_thread_);
Expand All @@ -177,6 +185,7 @@ class VideoRtpReceiver : public RtpReceiverInternal {
// or switched.
bool saved_generate_keyframe_ RTC_GUARDED_BY(worker_thread_) = false;
bool saved_encoded_sink_enabled_ RTC_GUARDED_BY(worker_thread_) = false;
const rtc::scoped_refptr<PendingTaskSafetyFlag> worker_thread_safety_;
};

} // namespace webrtc
Expand Down
13 changes: 13 additions & 0 deletions pc/video_track.cc
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,19 @@ VideoTrackSourceInterface* VideoTrack::GetSourceInternal() const {
return video_source_->internal();
}

void VideoTrack::set_should_receive(bool receive) {
RTC_DCHECK_RUN_ON(&signaling_thread_);
if (should_receive_ == receive)
return;
should_receive_ = receive;
Notifier<VideoTrackInterface>::FireOnChanged();
}

bool VideoTrack::should_receive() const {
RTC_DCHECK_RUN_ON(&signaling_thread_);
return should_receive_;
}

VideoTrackInterface::ContentHint VideoTrack::content_hint() const {
RTC_DCHECK_RUN_ON(&signaling_thread_);
return content_hint_;
Expand Down
4 changes: 4 additions & 0 deletions pc/video_track.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ class VideoTrack : public MediaStreamTrack<VideoTrackInterface>,
void RequestRefreshFrame() override;
VideoTrackSourceInterface* GetSource() const override;

void set_should_receive(bool should_receive) override;
bool should_receive() const override;

ContentHint content_hint() const override;
void set_content_hint(ContentHint hint) override;
bool set_enabled(bool enable) override;
Expand Down Expand Up @@ -81,6 +84,7 @@ class VideoTrack : public MediaStreamTrack<VideoTrackInterface>,
// be queried without blocking on the worker thread by callers that don't
// use an api proxy to call the `enabled()` method.
bool enabled_w_ RTC_GUARDED_BY(worker_thread_) = true;
bool should_receive_ RTC_GUARDED_BY(signaling_thread_) = true;
};

} // namespace webrtc
Expand Down
1 change: 1 addition & 0 deletions sdk/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ if (is_android) {
sources = [
"api/org/webrtc/DefaultVideoDecoderFactory.java",
"api/org/webrtc/DefaultVideoEncoderFactory.java",
"api/org/webrtc/WrappedVideoDecoderFactory.java",
]

deps = [
Expand Down
1 change: 1 addition & 0 deletions sdk/android/api/org/webrtc/PeerConnectionFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.webrtc.RtpCapabilities;
import org.webrtc.audio.AudioDeviceModule;
import org.webrtc.audio.JavaAudioDeviceModule;
import org.webrtc.RtpCapabilities;

/**
* Java wrapper for a C++ PeerConnectionFactoryInterface. Main entry point to
Expand Down
20 changes: 20 additions & 0 deletions sdk/android/api/org/webrtc/VideoTrack.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,24 @@ public void removeSink(VideoSink sink) {
}
}

/**
* For a remote video track, starts/stops receiving the video stream.
*
* If this is a local video track, this is a no-op.
*/
public void setShouldReceive(boolean shouldReceive){
nativeSetShouldReceive(getNativeMediaStreamTrack(), shouldReceive);
}

/**
* The current receive status for a remote video track.
*
* This has no meaning for a local video track.
*/
public boolean shouldReceive(){
return nativeGetShouldReceive(getNativeMediaStreamTrack());
}

@Override
public void dispose() {
for (long nativeSink : sinks.values()) {
Expand All @@ -73,4 +91,6 @@ public long getNativeVideoTrack() {
private static native void nativeRemoveSink(long track, long nativeSink);
private static native long nativeWrapSink(VideoSink sink);
private static native void nativeFreeSink(long sink);
private static native void nativeSetShouldReceive(long track, boolean shouldReceive);
private static native boolean nativeGetShouldReceive(long track);
}
75 changes: 75 additions & 0 deletions sdk/android/api/org/webrtc/WrappedVideoDecoderFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright 2023 LiveKit
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.webrtc;

import android.media.MediaCodecInfo;
import androidx.annotation.Nullable;

import java.util.Arrays;
import java.util.LinkedHashSet;

public class WrappedVideoDecoderFactory implements VideoDecoderFactory {
public WrappedVideoDecoderFactory(@Nullable EglBase.Context eglContext) {
this.hardwareVideoDecoderFactory = new HardwareVideoDecoderFactory(eglContext);
this.platformSoftwareVideoDecoderFactory = new PlatformSoftwareVideoDecoderFactory(eglContext);
}

private final VideoDecoderFactory hardwareVideoDecoderFactory;
private final VideoDecoderFactory hardwareVideoDecoderFactoryWithoutEglContext = new HardwareVideoDecoderFactory(null) ;
private final VideoDecoderFactory softwareVideoDecoderFactory = new SoftwareVideoDecoderFactory();
@Nullable
private final VideoDecoderFactory platformSoftwareVideoDecoderFactory;

@Override
public VideoDecoder createDecoder(VideoCodecInfo codecType) {
VideoDecoder softwareDecoder = this.softwareVideoDecoderFactory.createDecoder(codecType);
VideoDecoder hardwareDecoder = this.hardwareVideoDecoderFactory.createDecoder(codecType);
if (softwareDecoder == null && this.platformSoftwareVideoDecoderFactory != null) {
softwareDecoder = this.platformSoftwareVideoDecoderFactory.createDecoder(codecType);
}

if(hardwareDecoder != null && disableSurfaceTextureFrame(hardwareDecoder.getImplementationName())) {
hardwareDecoder.release();
hardwareDecoder = this.hardwareVideoDecoderFactoryWithoutEglContext.createDecoder(codecType);
}

if (hardwareDecoder != null && softwareDecoder != null) {
return new VideoDecoderFallback(softwareDecoder, hardwareDecoder);
} else {
return hardwareDecoder != null ? hardwareDecoder : softwareDecoder;
}
}

private boolean disableSurfaceTextureFrame(String name) {
if (name.startsWith("OMX.qcom.") || name.startsWith("OMX.hisi.")) {
return true;
}
return false;
}

@Override
public VideoCodecInfo[] getSupportedCodecs() {
LinkedHashSet<VideoCodecInfo> supportedCodecInfos = new LinkedHashSet();
supportedCodecInfos.addAll(Arrays.asList(this.softwareVideoDecoderFactory.getSupportedCodecs()));
supportedCodecInfos.addAll(Arrays.asList(this.hardwareVideoDecoderFactory.getSupportedCodecs()));
if (this.platformSoftwareVideoDecoderFactory != null) {
supportedCodecInfos.addAll(Arrays.asList(this.platformSoftwareVideoDecoderFactory.getSupportedCodecs()));
}

return (VideoCodecInfo[])supportedCodecInfos.toArray(new VideoCodecInfo[supportedCodecInfos.size()]);
}
}
1 change: 1 addition & 0 deletions sdk/android/src/jni/pc/peer_connection_factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "sdk/android/src/jni/logging/log_sink.h"
#include "sdk/android/src/jni/pc/android_network_monitor.h"
#include "sdk/android/src/jni/pc/audio.h"
#include "sdk/android/src/jni/pc/rtp_capabilities.h"
#include "sdk/android/src/jni/pc/ice_candidate.h"
#include "sdk/android/src/jni/pc/media_stream_track.h"
#include "sdk/android/src/jni/pc/owned_factory_and_threads.h"
Expand Down
11 changes: 11 additions & 0 deletions sdk/android/src/jni/video_track.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,16 @@ static void JNI_VideoTrack_FreeSink(JNIEnv* jni, jlong j_native_sink) {
delete reinterpret_cast<rtc::VideoSinkInterface<VideoFrame>*>(j_native_sink);
}

static void JNI_VideoTrack_SetShouldReceive(JNIEnv* jni,
jlong j_native_track,
jboolean should_receive) {
reinterpret_cast<VideoTrackInterface*>(j_native_track)->set_should_receive(should_receive);
}

static jboolean JNI_VideoTrack_GetShouldReceive(JNIEnv* jni,
jlong j_native_track) {
return reinterpret_cast<VideoTrackInterface*>(j_native_track)->should_receive();
}

} // namespace jni
} // namespace webrtc
3 changes: 3 additions & 0 deletions sdk/objc/api/peerconnection/RTCVideoTrack.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ RTC_OBJC_EXPORT
/** The video source for this video track. */
@property(nonatomic, readonly) RTC_OBJC_TYPE(RTCVideoSource) *source;

/** The receive state, if this is a remote video track. */
@property(nonatomic, assign) BOOL shouldReceive;

- (instancetype)init NS_UNAVAILABLE;

/** Register a renderer that will render all frames received on this track. */
Expand Down
Loading

0 comments on commit 9aaaab5

Please sign in to comment.