diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/ExampleActivity.java b/Android/APIExample/app/src/main/java/io/agora/api/example/ExampleActivity.java
index 7c51e7e4c..3d4907049 100644
--- a/Android/APIExample/app/src/main/java/io/agora/api/example/ExampleActivity.java
+++ b/Android/APIExample/app/src/main/java/io/agora/api/example/ExampleActivity.java
@@ -33,6 +33,7 @@
import io.agora.api.example.examples.advanced.SwitchExternalVideo;
import io.agora.api.example.examples.advanced.SetAudioProfile;
import io.agora.api.example.examples.advanced.MultiProcess;
+import io.agora.api.example.examples.advanced.FaceBeauty;
import io.agora.api.example.examples.advanced.VideoQuickSwitch;
import io.agora.api.example.examples.advanced.RTMPStreaming;
import io.agora.api.example.examples.advanced.StreamEncrypt;
@@ -158,6 +159,9 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
case R.id.action_mainFragment_raw_audio:
fragment = new ProcessAudioRawData();
break;
+ case R.id.action_mainFragment_video_enhancement:
+ fragment = new FaceBeauty();
+ break;
default:
fragment = new JoinChannelAudio();
break;
diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ARCore.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ARCore.java
index f9e7f9db4..bbb9ca94d 100644
--- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ARCore.java
+++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ARCore.java
@@ -294,7 +294,7 @@ private void joinChannel(String channelId)
}
// Set audio route to microPhone
- engine.setDefaultAudioRoutetoSpeakerphone(false);
+ engine.setDefaultAudioRoutetoSpeakerphone(true);
/** Sets the channel profile of the Agora RtcEngine.
CHANNEL_PROFILE_COMMUNICATION(0): (Default) The Communication profile.
diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ChannelEncryption.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ChannelEncryption.java
index 65afde86a..7c29b4869 100644
--- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ChannelEncryption.java
+++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ChannelEncryption.java
@@ -207,7 +207,7 @@ private void joinChannel(String channelId)
// Setup local video to render your local camera preview
engine.setupLocalVideo(new VideoCanvas(surfaceView, RENDER_MODE_HIDDEN, 0));
// Set audio route to microPhone
- engine.setDefaultAudioRoutetoSpeakerphone(false);
+ engine.setDefaultAudioRoutetoSpeakerphone(true);
/** Sets the channel profile of the Agora RtcEngine.
CHANNEL_PROFILE_COMMUNICATION(0): (Default) The Communication profile.
diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/CustomRemoteVideoRender.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/CustomRemoteVideoRender.java
index 6a15c308a..276dfa8fc 100644
--- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/CustomRemoteVideoRender.java
+++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/CustomRemoteVideoRender.java
@@ -174,7 +174,7 @@ private void joinChannel(String channelId) {
// Setup local video to render your local camera preview
engine.setupLocalVideo(new VideoCanvas(surfaceView, RENDER_MODE_HIDDEN, 0));
/**Set up to play remote sound with receiver*/
- engine.setDefaultAudioRoutetoSpeakerphone(false);
+ engine.setDefaultAudioRoutetoSpeakerphone(true);
engine.setEnableSpeakerphone(false);
/** Sets the channel profile of the Agora RtcEngine.
diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/FaceBeauty.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/FaceBeauty.java
new file mode 100644
index 000000000..302ea8775
--- /dev/null
+++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/FaceBeauty.java
@@ -0,0 +1,466 @@
+package io.agora.api.example.examples.advanced;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+import android.widget.Switch;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.yanzhenjie.permission.AndPermission;
+import com.yanzhenjie.permission.runtime.Permission;
+
+import io.agora.api.example.MainApplication;
+import io.agora.api.example.R;
+import io.agora.api.example.annotation.Example;
+import io.agora.api.example.common.BaseFragment;
+import io.agora.api.example.utils.CommonUtil;
+import io.agora.rtc.IRtcEngineEventHandler;
+import io.agora.rtc.RtcEngine;
+import io.agora.rtc.RtcEngineConfig;
+import io.agora.rtc.models.ChannelMediaOptions;
+import io.agora.rtc.video.BeautyOptions;
+import io.agora.rtc.video.VideoCanvas;
+import io.agora.rtc.video.VideoEncoderConfiguration;
+
+import static io.agora.api.example.common.model.Examples.ADVANCED;
+import static io.agora.rtc.Constants.CHANNEL_PROFILE_LIVE_BROADCASTING;
+import static io.agora.rtc.Constants.RENDER_MODE_HIDDEN;
+import static io.agora.rtc.video.VideoEncoderConfiguration.STANDARD_BITRATE;
+
+/**
+ * This demo demonstrates how to make a VideoProcessExtension
+ */
+@Example(
+ index = 28,
+ group = ADVANCED,
+ name = R.string.item_videoProcessExtension,
+ actionId = R.id.action_mainFragment_video_enhancement,
+ tipsId = R.string.videoEnhancement
+)
+public class FaceBeauty extends BaseFragment implements View.OnClickListener, CompoundButton.OnCheckedChangeListener, SeekBar.OnSeekBarChangeListener {
+ private static final String TAG = FaceBeauty.class.getSimpleName();
+
+ private FrameLayout fl_local, fl_remote;
+ private LinearLayout controlPanel;
+ private Button join;
+ private Switch beauty;
+ private SeekBar seek_lightness, seek_redness, seek_sharpness, seek_smoothness;
+ private EditText et_channel;
+ private RtcEngine engine;
+ private int myUid;
+ private boolean joined = false;
+ private BeautyOptions beautyOptions = new BeautyOptions();
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
+ {
+ View view = inflater.inflate(R.layout.fragment_video_enhancement, container, false);
+ return view;
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState)
+ {
+ super.onViewCreated(view, savedInstanceState);
+ join = view.findViewById(R.id.btn_join);
+ join.setOnClickListener(this);
+ et_channel = view.findViewById(R.id.et_channel);
+ fl_local = view.findViewById(R.id.fl_local);
+ fl_remote = view.findViewById(R.id.fl_remote);
+ controlPanel = view.findViewById(R.id.controlPanel);
+ beauty = view.findViewById(R.id.switch_face_beautify);
+ beauty.setOnCheckedChangeListener(this);
+ seek_lightness = view.findViewById(R.id.lightening);
+ seek_lightness.setOnSeekBarChangeListener(this);
+ seek_redness = view.findViewById(R.id.redness);
+ seek_redness.setOnSeekBarChangeListener(this);
+ seek_sharpness = view.findViewById(R.id.sharpness);
+ seek_sharpness.setOnSeekBarChangeListener(this);
+ seek_smoothness = view.findViewById(R.id.smoothness);
+ seek_smoothness.setOnSeekBarChangeListener(this);
+ }
+
+ @Override
+ public void onActivityCreated(@Nullable Bundle savedInstanceState)
+ {
+ super.onActivityCreated(savedInstanceState);
+ // Check if the context is valid
+ Context context = getContext();
+ if (context == null)
+ {
+ return;
+ }
+ try
+ {
+ RtcEngineConfig config = new RtcEngineConfig();
+ /**
+ * The context of Android Activity
+ */
+ config.mContext = context.getApplicationContext();
+ /**
+ * The App ID issued to you by Agora. See How to get the App ID
+ */
+ config.mAppId = getString(R.string.agora_app_id);
+ /**
+ * IRtcEngineEventHandler is an abstract class providing default implementation.
+ * The SDK uses this class to report to the app on SDK runtime events.
+ */
+ config.mEventHandler = iRtcEngineEventHandler;
+ engine = RtcEngine.create(config);
+ engine.setChannelProfile(CHANNEL_PROFILE_LIVE_BROADCASTING);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ getActivity().onBackPressed();
+ }
+ }
+
+ @Override
+ public void onDestroy()
+ {
+ super.onDestroy();
+ /**leaveChannel and Destroy the RtcEngine instance*/
+ if(engine != null)
+ {
+ engine.leaveChannel();
+ engine.stopPreview();
+ }
+ handler.post(RtcEngine::destroy);
+ engine = null;
+ }
+
+
+
+ private void joinChannel(String channelId)
+ {
+ // Check if the context is valid
+ Context context = getContext();
+ if (context == null)
+ {
+ return;
+ }
+
+ // Create render view by RtcEngine
+ SurfaceView surfaceView = RtcEngine.CreateRendererView(context);
+ if(fl_local.getChildCount() > 0)
+ {
+ fl_local.removeAllViews();
+ }
+ // Add to the local container
+ fl_local.addView(surfaceView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ // Setup local video to render your local camera preview
+ engine.setupLocalVideo(new VideoCanvas(surfaceView, RENDER_MODE_HIDDEN, 0));
+ // Set audio route to microPhone
+ engine.setDefaultAudioRoutetoSpeakerphone(true);
+
+ /**In the demo, the default is to enter as the anchor.*/
+ engine.setClientRole(IRtcEngineEventHandler.ClientRole.CLIENT_ROLE_BROADCASTER);
+ // Enable video module
+ engine.enableVideo();
+ // Setup video encoding configs
+ engine.setVideoEncoderConfiguration(new VideoEncoderConfiguration(
+ ((MainApplication)getActivity().getApplication()).getGlobalSettings().getVideoEncodingDimensionObject(),
+ VideoEncoderConfiguration.FRAME_RATE.valueOf(((MainApplication)getActivity().getApplication()).getGlobalSettings().getVideoEncodingFrameRate()),
+ STANDARD_BITRATE,
+ VideoEncoderConfiguration.ORIENTATION_MODE.valueOf(((MainApplication)getActivity().getApplication()).getGlobalSettings().getVideoEncodingOrientation())
+ ));
+
+ /**Please configure accessToken in the string_config file.
+ * A temporary token generated in Console. A temporary token is valid for 24 hours. For details, see
+ * https://docs.agora.io/en/Agora%20Platform/token?platform=All%20Platforms#get-a-temporary-token
+ * A token generated at the server. This applies to scenarios with high-security requirements. For details, see
+ * https://docs.agora.io/en/cloud-recording/token_server_java?platform=Java*/
+ String accessToken = getString(R.string.agora_access_token);
+ if (TextUtils.equals(accessToken, "") || TextUtils.equals(accessToken, "<#YOUR ACCESS TOKEN#>"))
+ {
+ accessToken = null;
+ }
+ /**
+ * enable face beauty by default
+ */
+ engine.startPreview();
+ /** Allows a user to join a channel.
+ if you do not specify the uid, we will generate the uid for you*/
+
+
+ ChannelMediaOptions option = new ChannelMediaOptions();
+ option.autoSubscribeAudio = true;
+ option.autoSubscribeVideo = true;
+ int res = engine.joinChannel(accessToken, channelId, "", 0);
+ if (res != 0)
+ {
+ // Usually happens with invalid parameters
+ // Error code description can be found at:
+ // en: https://docs.agora.io/en/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_error_code.html
+ // cn: https://docs.agora.io/cn/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_error_code.html
+ showAlert(RtcEngine.getErrorDescription(Math.abs(res)));
+ return;
+ }
+ // Prevent repeated entry
+ join.setEnabled(false);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v.getId() == R.id.btn_join)
+ {
+ if (!joined)
+ {
+ CommonUtil.hideInputBoard(getActivity(), et_channel);
+ // call when join button hit
+ String channelId = et_channel.getText().toString();
+ // Check permission
+ if (AndPermission.hasPermissions(this, Permission.Group.STORAGE, Permission.Group.MICROPHONE, Permission.Group.CAMERA))
+ {
+ joinChannel(channelId);
+ return;
+ }
+ // Request permission
+ AndPermission.with(this).runtime().permission(
+ Permission.Group.STORAGE,
+ Permission.Group.MICROPHONE,
+ Permission.Group.CAMERA
+ ).onGranted(permissions ->
+ {
+ // Permissions Granted
+ joinChannel(channelId);
+ }).start();
+ }
+ else
+ {
+ joined = false;
+ /**After joining a channel, the user must call the leaveChannel method to end the
+ * call before joining another channel. This method returns 0 if the user leaves the
+ * channel and releases all resources related to the call. This method call is
+ * asynchronous, and the user has not exited the channel when the method call returns.
+ * Once the user leaves the channel, the SDK triggers the onLeaveChannel callback.
+ * A successful leaveChannel method call triggers the following callbacks:
+ * 1:The local client: onLeaveChannel.
+ * 2:The remote client: onUserOffline, if the user leaving the channel is in the
+ * Communication channel, or is a BROADCASTER in the Live Broadcast profile.
+ * @returns 0: Success.
+ * < 0: Failure.
+ * PS:
+ * 1:If you call the destroy method immediately after calling the leaveChannel
+ * method, the leaveChannel process interrupts, and the SDK does not trigger
+ * the onLeaveChannel callback.
+ * 2:If you call the leaveChannel method during CDN live streaming, the SDK
+ * triggers the removeInjectStreamUrl method.*/
+ engine.leaveChannel();
+ engine.stopPreview();
+ join.setText(getString(R.string.join));
+ controlPanel.setVisibility(View.INVISIBLE);
+ }
+ }
+ }
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if(buttonView.getId() == beauty.getId()){
+ engine.setBeautyEffectOptions(isChecked, beautyOptions);
+ }
+ }
+
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ float value = ((float) progress) / 10;
+ if(seekBar.getId() == seek_lightness.getId()){
+ beautyOptions.lighteningLevel = value;
+ engine.setBeautyEffectOptions(beauty.isChecked(), beautyOptions);
+ }
+ else if(seekBar.getId() == seek_redness.getId()){
+ beautyOptions.rednessLevel = value;
+ engine.setBeautyEffectOptions(beauty.isChecked(), beautyOptions);
+ }
+ else if(seekBar.getId() == seek_sharpness.getId()){
+ beautyOptions.sharpnessLevel = value;
+ engine.setBeautyEffectOptions(beauty.isChecked(), beautyOptions);
+ }
+ else if(seekBar.getId() == seek_smoothness.getId()){
+ beautyOptions.smoothnessLevel = value;
+ engine.setBeautyEffectOptions(beauty.isChecked(), beautyOptions);
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+
+ }
+
+
+ /**
+ * IRtcEngineEventHandler is an abstract class providing default implementation.
+ * The SDK uses this class to report to the app on SDK runtime events.
+ */
+ private final IRtcEngineEventHandler iRtcEngineEventHandler = new IRtcEngineEventHandler()
+ {
+ /**Reports a warning during SDK runtime.
+ * Warning code: https://docs.agora.io/en/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_warn_code.html*/
+ @Override
+ public void onWarning(int warn)
+ {
+ Log.w(TAG, String.format("onWarning code %d message %s", warn, RtcEngine.getErrorDescription(warn)));
+ }
+
+ /**Occurs when a user leaves the channel.
+ * @param stats With this callback, the application retrieves the channel information,
+ * such as the call duration and statistics.*/
+ @Override
+ public void onLeaveChannel(RtcStats stats)
+ {
+ super.onLeaveChannel(stats);
+ Log.i(TAG, String.format("local user %d leaveChannel!", myUid));
+ showLongToast(String.format("local user %d leaveChannel!", myUid));
+ }
+
+ /**Occurs when the local user joins a specified channel.
+ * The channel name assignment is based on channelName specified in the joinChannel method.
+ * If the uid is not specified when joinChannel is called, the server automatically assigns a uid.
+ * @param channel Channel name
+ * @param uid User ID
+ * @param elapsed Time elapsed (ms) from the user calling joinChannel until this callback is triggered*/
+ @Override
+ public void onJoinChannelSuccess(String channel, int uid, int elapsed)
+ {
+ Log.i(TAG, String.format("onJoinChannelSuccess channel %s uid %d", channel, uid));
+ showLongToast(String.format("onJoinChannelSuccess channel %s uid %d", channel, uid));
+ myUid = uid;
+ joined = true;
+ handler.post(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ join.setEnabled(true);
+ join.setText(getString(R.string.leave));
+ controlPanel.setVisibility(View.VISIBLE);
+ }
+ });
+ }
+
+ /**Since v2.9.0.
+ * Occurs when the remote video state changes.
+ * PS: This callback does not work properly when the number of users (in the Communication
+ * profile) or broadcasters (in the Live-broadcast profile) in the channel exceeds 17.
+ * @param uid ID of the remote user whose video state changes.
+ * @param state State of the remote video:
+ * REMOTE_VIDEO_STATE_STOPPED(0): The remote video is in the default state, probably due
+ * to REMOTE_VIDEO_STATE_REASON_LOCAL_MUTED(3), REMOTE_VIDEO_STATE_REASON_REMOTE_MUTED(5),
+ * or REMOTE_VIDEO_STATE_REASON_REMOTE_OFFLINE(7).
+ * REMOTE_VIDEO_STATE_STARTING(1): The first remote video packet is received.
+ * REMOTE_VIDEO_STATE_DECODING(2): The remote video stream is decoded and plays normally,
+ * probably due to REMOTE_VIDEO_STATE_REASON_NETWORK_RECOVERY (2),
+ * REMOTE_VIDEO_STATE_REASON_LOCAL_UNMUTED(4), REMOTE_VIDEO_STATE_REASON_REMOTE_UNMUTED(6),
+ * or REMOTE_VIDEO_STATE_REASON_AUDIO_FALLBACK_RECOVERY(9).
+ * REMOTE_VIDEO_STATE_FROZEN(3): The remote video is frozen, probably due to
+ * REMOTE_VIDEO_STATE_REASON_NETWORK_CONGESTION(1) or REMOTE_VIDEO_STATE_REASON_AUDIO_FALLBACK(8).
+ * REMOTE_VIDEO_STATE_FAILED(4): The remote video fails to start, probably due to
+ * REMOTE_VIDEO_STATE_REASON_INTERNAL(0).
+ * @param reason The reason of the remote video state change:
+ * REMOTE_VIDEO_STATE_REASON_INTERNAL(0): Internal reasons.
+ * REMOTE_VIDEO_STATE_REASON_NETWORK_CONGESTION(1): Network congestion.
+ * REMOTE_VIDEO_STATE_REASON_NETWORK_RECOVERY(2): Network recovery.
+ * REMOTE_VIDEO_STATE_REASON_LOCAL_MUTED(3): The local user stops receiving the remote
+ * video stream or disables the video module.
+ * REMOTE_VIDEO_STATE_REASON_LOCAL_UNMUTED(4): The local user resumes receiving the remote
+ * video stream or enables the video module.
+ * REMOTE_VIDEO_STATE_REASON_REMOTE_MUTED(5): The remote user stops sending the video
+ * stream or disables the video module.
+ * REMOTE_VIDEO_STATE_REASON_REMOTE_UNMUTED(6): The remote user resumes sending the video
+ * stream or enables the video module.
+ * REMOTE_VIDEO_STATE_REASON_REMOTE_OFFLINE(7): The remote user leaves the channel.
+ * REMOTE_VIDEO_STATE_REASON_AUDIO_FALLBACK(8): The remote media stream falls back to the
+ * audio-only stream due to poor network conditions.
+ * REMOTE_VIDEO_STATE_REASON_AUDIO_FALLBACK_RECOVERY(9): The remote media stream switches
+ * back to the video stream after the network conditions improve.
+ * @param elapsed Time elapsed (ms) from the local user calling the joinChannel method until
+ * the SDK triggers this callback.*/
+ @Override
+ public void onRemoteVideoStateChanged(int uid, int state, int reason, int elapsed)
+ {
+ super.onRemoteVideoStateChanged(uid, state, reason, elapsed);
+ Log.i(TAG, "onRemoteVideoStateChanged->" + uid + ", state->" + state + ", reason->" + reason);
+ }
+
+ /**Occurs when a remote user (Communication)/host (Live Broadcast) joins the channel.
+ * @param uid ID of the user whose audio state changes.
+ * @param elapsed Time delay (ms) from the local user calling joinChannel/setClientRole
+ * until this callback is triggered.*/
+ @Override
+ public void onUserJoined(int uid, int elapsed)
+ {
+ super.onUserJoined(uid, elapsed);
+ Log.i(TAG, "onUserJoined->" + uid);
+ showLongToast(String.format("user %d joined!", uid));
+ /**Check if the context is correct*/
+ Context context = getContext();
+ if (context == null) {
+ return;
+ }
+ else{
+ handler.post(() ->
+ {
+ if(fl_remote.getChildCount() > 0){
+ fl_remote.removeAllViews();
+ }
+ /**Display remote video stream*/
+ SurfaceView surfaceView = null;
+ // Create render view by RtcEngine
+ surfaceView = new SurfaceView(context);
+ surfaceView.setZOrderMediaOverlay(true);
+ // Add to the remote container
+ fl_remote.addView(surfaceView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ // Setup remote video to render
+ engine.setupRemoteVideo(new VideoCanvas(surfaceView, RENDER_MODE_HIDDEN, uid));
+ });
+ }
+ }
+
+ /**Occurs when a remote user (Communication)/host (Live Broadcast) leaves the channel.
+ * @param uid ID of the user whose audio state changes.
+ * @param reason Reason why the user goes offline:
+ * USER_OFFLINE_QUIT(0): The user left the current channel.
+ * USER_OFFLINE_DROPPED(1): The SDK timed out and the user dropped offline because no data
+ * packet was received within a certain period of time. If a user quits the
+ * call and the message is not passed to the SDK (due to an unreliable channel),
+ * the SDK assumes the user dropped offline.
+ * USER_OFFLINE_BECOME_AUDIENCE(2): (Live broadcast only.) The client role switched from
+ * the host to the audience.*/
+ @Override
+ public void onUserOffline(int uid, int reason)
+ {
+ Log.i(TAG, String.format("user %d offline! reason:%d", uid, reason));
+ showLongToast(String.format("user %d offline! reason:%d", uid, reason));
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ /**Clear render view
+ Note: The video will stay at its last frame, to completely remove it you will need to
+ remove the SurfaceView from its parent*/
+ engine.setupRemoteVideo(new VideoCanvas(null, RENDER_MODE_HIDDEN, uid));
+ }
+ });
+ }
+ };
+}
diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/GeoFencing.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/GeoFencing.java
index fe78b663c..dca4be4f7 100644
--- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/GeoFencing.java
+++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/GeoFencing.java
@@ -207,7 +207,7 @@ private void joinChannel(String channelId) {
// Setup local video to render your local camera preview
engine.setupLocalVideo(new VideoCanvas(surfaceView, RENDER_MODE_HIDDEN, 0));
// Set audio route to microPhone
- engine.setDefaultAudioRoutetoSpeakerphone(false);
+ engine.setDefaultAudioRoutetoSpeakerphone(true);
/** Sets the channel profile of the Agora RtcEngine.
CHANNEL_PROFILE_COMMUNICATION(0): (Default) The Communication profile.
diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/HostAcrossChannel.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/HostAcrossChannel.java
index ad833be99..e173cdcbc 100644
--- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/HostAcrossChannel.java
+++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/HostAcrossChannel.java
@@ -225,7 +225,7 @@ private void joinChannel(String channelId)
// Setup local video to render your local camera preview
engine.setupLocalVideo(new VideoCanvas(surfaceView, RENDER_MODE_HIDDEN, 0));
// Set audio route to microPhone
- engine.setDefaultAudioRoutetoSpeakerphone(false);
+ engine.setDefaultAudioRoutetoSpeakerphone(true);
/** Sets the channel profile of the Agora RtcEngine.
CHANNEL_PROFILE_COMMUNICATION(0): (Default) The Communication profile.
diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/InCallReport.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/InCallReport.java
index 7a2b1e641..ac6aa24d0 100644
--- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/InCallReport.java
+++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/InCallReport.java
@@ -203,7 +203,7 @@ private void joinChannel(String channelId)
// Setup local video to render your local camera preview
engine.setupLocalVideo(new VideoCanvas(surfaceView, RENDER_MODE_HIDDEN, 0));
// Set audio route to microPhone
- engine.setDefaultAudioRoutetoSpeakerphone(false);
+ engine.setDefaultAudioRoutetoSpeakerphone(true);
/** Sets the channel profile of the Agora RtcEngine.
CHANNEL_PROFILE_COMMUNICATION(0): (Default) The Communication profile.
diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/JoinMultipleChannel.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/JoinMultipleChannel.java
index 3caf3f143..67a2aba4f 100644
--- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/JoinMultipleChannel.java
+++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/JoinMultipleChannel.java
@@ -200,7 +200,7 @@ private void setupVideo()
engine.setupLocalVideo(new VideoCanvas(surfaceView, RENDER_MODE_HIDDEN, 0));
engine.startPreview();
// Set audio route to microPhone
- engine.setDefaultAudioRoutetoSpeakerphone(false);
+ engine.setDefaultAudioRoutetoSpeakerphone(true);
/** Sets the channel profile of the Agora RtcEngine.
CHANNEL_PROFILE_COMMUNICATION(0): (Default) The Communication profile.
diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/LiveStreaming.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/LiveStreaming.java
index 2600c1c66..e35843b90 100644
--- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/LiveStreaming.java
+++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/LiveStreaming.java
@@ -232,7 +232,7 @@ private void joinChannel(String channelId) {
// Setup local video to render your local camera preview
engine.setupLocalVideo(new VideoCanvas(surfaceView, RENDER_MODE_HIDDEN, 0));
// Set audio route to microPhone
- engine.setDefaultAudioRoutetoSpeakerphone(false);
+ engine.setDefaultAudioRoutetoSpeakerphone(true);
/** Sets the channel profile of the Agora RtcEngine.
CHANNEL_PROFILE_COMMUNICATION(0): (Default) The Communication profile.
diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/MediaPlayerKit.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/MediaPlayerKit.java
index 2fb4238a4..b3ac96898 100644
--- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/MediaPlayerKit.java
+++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/MediaPlayerKit.java
@@ -310,7 +310,7 @@ private void joinChannel(String channelId) {
return;
}
- engine.setDefaultAudioRoutetoSpeakerphone(false);
+ engine.setDefaultAudioRoutetoSpeakerphone(true);
/** Sets the channel profile of the Agora RtcEngine.
CHANNEL_PROFILE_COMMUNICATION(0): (Default) The Communication profile.
diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ProcessRawData.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ProcessRawData.java
index d35a128be..dfdaa5cb1 100644
--- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ProcessRawData.java
+++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ProcessRawData.java
@@ -218,7 +218,7 @@ private void joinChannel(String channelId) {
VideoEncoderConfiguration.ORIENTATION_MODE.valueOf(((MainApplication)getActivity().getApplication()).getGlobalSettings().getVideoEncodingOrientation())
));
/**Set up to play remote sound with receiver*/
- engine.setDefaultAudioRoutetoSpeakerphone(false);
+ engine.setDefaultAudioRoutetoSpeakerphone(true);
engine.setEnableSpeakerphone(false);
/**
diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/PushExternalVideo.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/PushExternalVideo.java
index 88e141ad4..f7597d12a 100644
--- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/PushExternalVideo.java
+++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/PushExternalVideo.java
@@ -202,7 +202,7 @@ private void joinChannel(String channelId) {
fl_local.addView(textureView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
/**Set up to play remote sound with receiver*/
- engine.setDefaultAudioRoutetoSpeakerphone(false);
+ engine.setDefaultAudioRoutetoSpeakerphone(true);
engine.setEnableSpeakerphone(false);
/** Sets the channel profile of the Agora RtcEngine.
diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/RTMPStreaming.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/RTMPStreaming.java
index 087918362..1de8e3993 100644
--- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/RTMPStreaming.java
+++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/RTMPStreaming.java
@@ -10,7 +10,6 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
-import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
@@ -22,7 +21,6 @@
import com.yanzhenjie.permission.AndPermission;
import com.yanzhenjie.permission.runtime.Permission;
-import io.agora.api.component.Constant;
import io.agora.api.example.MainApplication;
import io.agora.api.example.R;
import io.agora.api.example.annotation.Example;
@@ -33,7 +31,6 @@
import io.agora.rtc.RtcEngine;
import io.agora.rtc.live.LiveTranscoding;
import io.agora.rtc.models.ChannelMediaOptions;
-import io.agora.rtc.video.AgoraImage;
import io.agora.rtc.video.VideoCanvas;
import io.agora.rtc.video.VideoEncoderConfiguration;
@@ -43,9 +40,13 @@
import static io.agora.rtc.Constants.ERR_PUBLISH_STREAM_INTERNAL_SERVER_ERROR;
import static io.agora.rtc.Constants.ERR_PUBLISH_STREAM_NOT_FOUND;
import static io.agora.rtc.Constants.ERR_TIMEDOUT;
+import static io.agora.rtc.Constants.RTMP_STREAM_PUBLISH_ERROR_CONNECTION_TIMEOUT;
+import static io.agora.rtc.Constants.RTMP_STREAM_PUBLISH_ERROR_INTERNAL_SERVER_ERROR;
+import static io.agora.rtc.Constants.RTMP_STREAM_PUBLISH_ERROR_NET_DOWN;
+import static io.agora.rtc.Constants.RTMP_STREAM_PUBLISH_ERROR_OK;
+import static io.agora.rtc.Constants.RTMP_STREAM_PUBLISH_ERROR_RTMP_SERVER_ERROR;
+import static io.agora.rtc.Constants.RTMP_STREAM_PUBLISH_ERROR_STREAM_NOT_FOUND;
import static io.agora.rtc.video.VideoCanvas.RENDER_MODE_HIDDEN;
-import static io.agora.rtc.video.VideoEncoderConfiguration.FRAME_RATE.FRAME_RATE_FPS_15;
-import static io.agora.rtc.video.VideoEncoderConfiguration.ORIENTATION_MODE.ORIENTATION_MODE_ADAPTIVE;
import static io.agora.rtc.video.VideoEncoderConfiguration.STANDARD_BITRATE;
import static io.agora.rtc.video.VideoEncoderConfiguration.VD_640x360;
@@ -75,7 +76,7 @@ public class RTMPStreaming extends BaseFragment implements View.OnClickListener
private int myUid;
private boolean joined = false, publishing = false;
private VideoEncoderConfiguration.VideoDimensions dimensions = VD_640x360;
- private LiveTranscoding transcoding;
+ private LiveTranscoding transcoding = new LiveTranscoding();
private static final Integer MAX_RETRY_TIMES = 3;
private int retried = 0;
private boolean unpublishing = false;
@@ -219,7 +220,7 @@ private void joinChannel(String channelId) {
VideoEncoderConfiguration.ORIENTATION_MODE.valueOf(((MainApplication)getActivity().getApplication()).getGlobalSettings().getVideoEncodingOrientation())
));
/**Set up to play remote sound with receiver*/
- engine.setDefaultAudioRoutetoSpeakerphone(false);
+ engine.setDefaultAudioRoutetoSpeakerphone(true);
engine.setEnableSpeakerphone(false);
/**Please configure accessToken in the string_config file.
@@ -254,7 +255,6 @@ private void startPublish() {
if (transCodeSwitch.isChecked()) {
/**LiveTranscoding: A class for managing user-specific CDN live audio/video transcoding settings.
* See */
- transcoding = new LiveTranscoding();
transcoding.width = dimensions.height;
transcoding.height = dimensions.width;
/**The transcodingUser class which defines the video properties of the user displaying the
@@ -271,49 +271,9 @@ private void startPublish() {
* 0: Success.
* <0: Failure.*/
int ret = transcoding.addUser(localTranscodingUser);
- /**Sets the video layout and audio settings for CDN live.
- * The SDK triggers the onTranscodingUpdated callback when you call this method to update
- * the LiveTranscodingclass. If you call this method to set the LiveTranscoding class for
- * the first time, the SDK does not trigger the onTranscodingUpdated callback.
- * @param transcoding Sets the CDN live audio/video transcoding settings See
- *
- * @return
- * 0: Success.
- * <0: Failure.
- * PS:
- * This method applies to Live Broadcast only.
- * Ensure that you enable the RTMP Converter service before using this function. See
- * Prerequisites in Push Streams to CDN.
- * Ensure that you call the setClientRole method and set the user role as the host.
- * Ensure that you call the setLiveTranscoding method before calling the addPublishStreamUrl method.*/
- engine.setLiveTranscoding(transcoding);
}
- /**Publishes the local stream to the CDN.
- * The addPublishStreamUrl method call triggers the onRtmpStreamingStateChanged callback on
- * the local client to report the state of adding a local stream to the CDN.
- * @param url The CDN streaming URL in the RTMP format. The maximum length of this parameter
- * is 1024 bytes. The URL address must not contain special characters, such as
- * Chinese language characters.
- * @param transcodingEnabled Sets whether transcoding is enabled/disabled. If you set this
- * parameter as true, ensure that you call the setLiveTranscoding
- * method before this method.
- * true: Enable transcoding. To transcode the audio or video
- * streams when publishing them to CDN live, often used for
- * combining the audio and video streams of multiple hosts in CDN live.
- * false: Disable transcoding.
- * @return
- * 0: Success.
- * < 0: Failure.
- * ERR_INVALID_ARGUMENT(2): Invalid parameter, usually because the URL address is null or the string length is 0.
- * ERR_NOT_INITIALIZED(7): You have not initialized RtcEngine when publishing the stream.
- * PS:
- * Ensure that you enable the RTMP Converter service before using this function. See
- * Prerequisites in Push Streams to CDN.
- * This method applies to Live Broadcast only.
- * Ensure that the user joins a channel before calling this method.
- * This method adds only one stream HTTP/HTTPS URL address each time it is called.*/
- int code = engine.addPublishStreamUrl(et_url.getText().toString(), transCodeSwitch.isChecked());
- if(code == 0){
+
+ if(startRtmpStreaming() == 0){
retryTask = new AsyncTask() {
@Override
protected Object doInBackground(Object[] objects) {
@@ -325,7 +285,7 @@ protected Object doInBackground(Object[] objects) {
Log.e(TAG, e.getMessage());
break;
}
- result = engine.addPublishStreamUrl(et_url.getText().toString(), transCodeSwitch.isChecked());
+ result = startRtmpStreaming();
}
return result;
}
@@ -338,6 +298,17 @@ protected Object doInBackground(Object[] objects) {
transCodeSwitch.setEnabled(false);
}
+ private int startRtmpStreaming(){
+ int code;
+ if(transCodeSwitch.isChecked()){
+ code = engine.startRtmpStreamWithTranscoding(et_url.getText().toString(), transcoding);
+ }
+ else {
+ code = engine.startRtmpStreamWithoutTranscoding(et_url.getText().toString());
+ }
+ return code;
+ }
+
private void stopPublish() {
/**Removes an RTMP stream from the CDN.
* This method removes the RTMP URL address (added by addPublishStreamUrl) from a CDN live
@@ -356,7 +327,7 @@ private void stopPublish() {
* This method removes only one stream RTMP URL address each time it is called.*/
unpublishing = true;
retryTask.cancel(true);
- int ret = engine.removePublishStreamUrl(et_url.getText().toString());
+ int ret = engine.stopRtmpStream(et_url.getText().toString());
}
/**
@@ -517,7 +488,7 @@ public void onRemoteVideoStateChanged(int uid, int state, int reason, int elapse
* RTMP_STREAM_PUBLISH_STATE_FAILURE(4): The RTMP streaming fails. See the errCode parameter
* for the detailed error information. You can also call the addPublishStreamUrl
* method to publish the RTMP streaming again.
- * @param errCode The detailed error information for streaming:
+ * @param errorType The detailed error information for streaming:
* RTMP_STREAM_PUBLISH_ERROR_OK(0): The RTMP streaming publishes successfully.
* RTMP_STREAM_PUBLISH_ERROR_INVALID_ARGUMEN(1): Invalid argument used. If, for example,
* you do not call the setLiveTranscoding method to configure the LiveTranscoding
@@ -540,80 +511,59 @@ public void onRemoteVideoStateChanged(int uid, int state, int reason, int elapse
* RTMP_STREAM_PUBLISH_ERROR_FORMAT_NOT_SUPPORTED(10): The format of the RTMP streaming
* URL is not supported. Check whether the URL format is correct.*/
@Override
- public void onRtmpStreamingStateChanged(String url, int state, int errCode) {
- super.onRtmpStreamingStateChanged(url, state, errCode);
- Log.i(TAG, "onRtmpStreamingStateChanged->" + url + ", state->" + state + ", errCode->" + errCode);
+ public void onRtmpStreamingStateChanged(String url, int state, int errorType) {
+ super.onRtmpStreamingStateChanged(url, state, errorType);
+ Log.i(TAG, "onRtmpStreamingStateChanged->" + url + ", state->" + state + ", errorType->" + errorType);
if (state == Constants.RTMP_STREAM_PUBLISH_STATE_RUNNING) {
/**After confirming the successful push, make changes to the UI.*/
- publishing = true;
- retryTask.cancel(true);
- handler.post(() -> {
- publish.setEnabled(true);
- publish.setText(getString(R.string.stoppublish));
- });
+ if(errorType == RTMP_STREAM_PUBLISH_ERROR_OK){
+ publishing = true;
+ retried = 0;
+ retryTask.cancel(true);
+ handler.post(() -> {
+ publish.setEnabled(true);
+ publish.setText(getString(R.string.stoppublish));
+ });
+ }
} else if (state == Constants.RTMP_STREAM_PUBLISH_STATE_FAILURE) {
- /**if failed, make changes to the UI.*/
- publishing = true;
- retryTask.cancel(true);
- handler.post(() -> {
- publish.setEnabled(true);
- publish.setText(getString(R.string.publish));
- transCodeSwitch.setEnabled(true);
- publishing = false;
- });
+ engine.stopRtmpStream(et_url.getText().toString());
+ if((errorType == RTMP_STREAM_PUBLISH_ERROR_CONNECTION_TIMEOUT
+ || errorType == RTMP_STREAM_PUBLISH_ERROR_INTERNAL_SERVER_ERROR
+ || errorType == RTMP_STREAM_PUBLISH_ERROR_RTMP_SERVER_ERROR
+ || errorType == RTMP_STREAM_PUBLISH_ERROR_STREAM_NOT_FOUND
+ || errorType == RTMP_STREAM_PUBLISH_ERROR_NET_DOWN))
+ {
+ /**need republishing.*/
+ Log.w(TAG, "RTMP publish failure ->" + url + ", state->" + state + ", errorType->" + errorType);
+ }
+ else{
+ /**Other failures which can't be recover by republishing, make changes to the UI.*/
+ retryTask.cancel(true);
+ unpublishing = true;
+ }
} else if (state == Constants.RTMP_STREAM_PUBLISH_STATE_IDLE) {
- /**Push stream not started or ended, make changes to the UI.*/
- publishing = true;
- handler.post(() -> {
- publish.setEnabled(true);
- publish.setText(getString(R.string.publish));
- transCodeSwitch.setEnabled(true);
- publishing = false;
- });
- }
- }
-
- /**
- * Reports the result of calling the removePublishStreamUrl method.
- * This callback indicates whether you have successfully removed an RTMP or RTMPS stream from the CDN.
- * @param url The CDN streaming URL.
- */
- @Override
- public void onStreamUnpublished(String url) {
- if(url != null && !unpublishing && retried < MAX_RETRY_TIMES){
- engine.addPublishStreamUrl(et_url.getText().toString(), transCodeSwitch.isChecked());
- retried++;
- }
- if(unpublishing){
- unpublishing = false;
- }
- }
-
- /**
- * Reports the result of calling the addPublishStreamUrl method.
- * This callback indicates whether you have successfully added an RTMP or RTMPS stream to the CDN.
- * @param url The CDN streaming URL.
- * @param error The detailed error information:
- */
- @Override
- public void onStreamPublished(String url, int error) {
- if(error == ERR_OK){
- retried = 0;
- retryTask.cancel(true);
- }
- else{
- switch (error){
- case ERR_FAILED:
- case ERR_TIMEDOUT:
- case ERR_PUBLISH_STREAM_INTERNAL_SERVER_ERROR:
- engine.removePublishStreamUrl(url);
- break;
- case ERR_PUBLISH_STREAM_NOT_FOUND:
- if(retried < MAX_RETRY_TIMES){
- engine.addPublishStreamUrl(et_url.getText().toString(), transCodeSwitch.isChecked());
- retried++;
- }
- break;
+ if(unpublishing){
+ unpublishing = false;
+ /**Push stream not started or ended, make changes to the UI.*/
+ handler.post(() -> {
+ publish.setEnabled(true);
+ publish.setText(getString(R.string.publish));
+ transCodeSwitch.setEnabled(true);
+ });
+ }
+ else if( retried >= MAX_RETRY_TIMES){
+ retryTask.cancel(true);
+ retried = 0;
+ /**Push stream not started or ended, make changes to the UI.*/
+ handler.post(() -> {
+ publish.setEnabled(true);
+ publish.setText(getString(R.string.publish));
+ transCodeSwitch.setEnabled(true);
+ });
+ }
+ else{
+ retried++;
+ startRtmpStreaming();
}
}
}
@@ -655,9 +605,9 @@ public void onUserJoined(int uid, int elapsed) {
transcodingUser.width = transcoding.width;
transcodingUser.height = transcoding.height / MAXUserCount;
transcodingUser.uid = uid;
- int ret = transcoding.addUser(transcodingUser);
+ transcoding.addUser(transcodingUser);
/**refresh transCoding configuration*/
- engine.setLiveTranscoding(transcoding);
+ int ret = engine.updateRtmpTranscoding(transcoding);
}
}
@@ -698,7 +648,7 @@ public void run() {
int code = transcoding.removeUser(uid);
if (code == ERR_OK) {
/**refresh transCoding configuration*/
- engine.setLiveTranscoding(transcoding);
+ engine.updateRtmpTranscoding(transcoding);
}
}
}
diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SendDataStream.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SendDataStream.java
index 392937edf..a686be99c 100644
--- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SendDataStream.java
+++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SendDataStream.java
@@ -222,7 +222,7 @@ private void joinChannel(String channelId)
VideoEncoderConfiguration.ORIENTATION_MODE.valueOf(((MainApplication)getActivity().getApplication()).getGlobalSettings().getVideoEncodingOrientation())
));
/**Set up to play remote sound with receiver*/
- engine.setDefaultAudioRoutetoSpeakerphone(false);
+ engine.setDefaultAudioRoutetoSpeakerphone(true);
engine.setEnableSpeakerphone(false);
/**Please configure accessToken in the string_config file.
diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SetVideoProfile.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SetVideoProfile.java
index 24a73ed81..664ccacf5 100644
--- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SetVideoProfile.java
+++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SetVideoProfile.java
@@ -250,7 +250,7 @@ private void joinChannel(String channelId)
// Setup local video to render your local camera preview
engine.setupLocalVideo(new VideoCanvas(surfaceView, RENDER_MODE_HIDDEN, 0));
// Set audio route to microPhone
- engine.setDefaultAudioRoutetoSpeakerphone(false);
+ engine.setDefaultAudioRoutetoSpeakerphone(true);
/** Sets the channel profile of the Agora RtcEngine.
CHANNEL_PROFILE_COMMUNICATION(0): (Default) The Communication profile.
diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/StreamEncrypt.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/StreamEncrypt.java
index 1d38e8b64..02775f025 100644
--- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/StreamEncrypt.java
+++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/StreamEncrypt.java
@@ -190,7 +190,7 @@ private void joinChannel(String channelId)
// Setup local video to render your local camera preview
engine.setupLocalVideo(new VideoCanvas(surfaceView, RENDER_MODE_HIDDEN, 0));
/**Set up to play remote sound with receiver*/
- engine.setDefaultAudioRoutetoSpeakerphone(false);
+ engine.setDefaultAudioRoutetoSpeakerphone(true);
engine.setEnableSpeakerphone(false);
/** Sets the channel profile of the Agora RtcEngine.
diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SuperResolution.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SuperResolution.java
index 36a9b55ff..df3d2c78e 100644
--- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SuperResolution.java
+++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SuperResolution.java
@@ -196,7 +196,7 @@ private void joinChannel(String channelId)
// Setup local video to render your local camera preview
engine.setupLocalVideo(new VideoCanvas(surfaceView, RENDER_MODE_HIDDEN, 0));
// Set audio route to microPhone
- engine.setDefaultAudioRoutetoSpeakerphone(false);
+ engine.setDefaultAudioRoutetoSpeakerphone(true);
/** Sets the channel profile of the Agora RtcEngine.
CHANNEL_PROFILE_COMMUNICATION(0): (Default) The Communication profile.
diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SwitchExternalVideo.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SwitchExternalVideo.java
index af686e580..a74abed71 100644
--- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SwitchExternalVideo.java
+++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SwitchExternalVideo.java
@@ -292,7 +292,7 @@ private void joinChannel(String channelId) {
/**Enable video module*/
ENGINE.enableVideo();
/**Set up to play remote sound with receiver*/
- ENGINE.setDefaultAudioRoutetoSpeakerphone(false);
+ ENGINE.setDefaultAudioRoutetoSpeakerphone(true);
ENGINE.setEnableSpeakerphone(false);
/**Please configure accessToken in the string_config file.
diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/VideoMetadata.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/VideoMetadata.java
index 7104358fb..11ea6eb93 100644
--- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/VideoMetadata.java
+++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/VideoMetadata.java
@@ -225,7 +225,7 @@ private void joinChannel(String channelId)
VideoEncoderConfiguration.ORIENTATION_MODE.valueOf(((MainApplication)getActivity().getApplication()).getGlobalSettings().getVideoEncodingOrientation())
));
/**Set up to play remote sound with receiver*/
- engine.setDefaultAudioRoutetoSpeakerphone(false);
+ engine.setDefaultAudioRoutetoSpeakerphone(true);
engine.setEnableSpeakerphone(false);
/**register metadata observer
diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/VideoQuickSwitch.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/VideoQuickSwitch.java
index 1de8ab193..2559c15bf 100644
--- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/VideoQuickSwitch.java
+++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/VideoQuickSwitch.java
@@ -261,7 +261,7 @@ private final void joinChannel(String channelId)
VideoEncoderConfiguration.ORIENTATION_MODE.valueOf(((MainApplication)getActivity().getApplication()).getGlobalSettings().getVideoEncodingOrientation())
));
/**Set up to play remote sound with receiver*/
- engine.setDefaultAudioRoutetoSpeakerphone(false);
+ engine.setDefaultAudioRoutetoSpeakerphone(true);
engine.setEnableSpeakerphone(false);
/**Please configure accessToken in the string_config file.
diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/basic/JoinChannelVideo.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/basic/JoinChannelVideo.java
index d65198fb3..bb1cac0f1 100644
--- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/basic/JoinChannelVideo.java
+++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/basic/JoinChannelVideo.java
@@ -217,7 +217,7 @@ private void joinChannel(String channelId)
// Setup local video to render your local camera preview
engine.setupLocalVideo(new VideoCanvas(surfaceView, RENDER_MODE_HIDDEN, 0));
// Set audio route to microPhone
- engine.setDefaultAudioRoutetoSpeakerphone(false);
+ engine.setDefaultAudioRoutetoSpeakerphone(true);
/** Sets the channel profile of the Agora RtcEngine.
CHANNEL_PROFILE_COMMUNICATION(0): (Default) The Communication profile.
diff --git a/Android/APIExample/app/src/main/res/layout/fragment_video_enhancement.xml b/Android/APIExample/app/src/main/res/layout/fragment_video_enhancement.xml
new file mode 100644
index 000000000..135a9fba3
--- /dev/null
+++ b/Android/APIExample/app/src/main/res/layout/fragment_video_enhancement.xml
@@ -0,0 +1,184 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Android/APIExample/app/src/main/res/navigation/nav_graph.xml b/Android/APIExample/app/src/main/res/navigation/nav_graph.xml
index 05bab2085..2085c0933 100755
--- a/Android/APIExample/app/src/main/res/navigation/nav_graph.xml
+++ b/Android/APIExample/app/src/main/res/navigation/nav_graph.xml
@@ -20,9 +20,6 @@
-
@@ -56,9 +53,6 @@
-
@@ -107,6 +101,9 @@
+
-
-
+
此示例演示了在音视频通话过程中如何伴随视频帧发送数据流信息的方法。
此示例演示了在音频通话过程中如何通过回调获取裸数据的功能。
音频耳返
+ 此示例演示了音频通话过程中SDK内置的各类视频增强功能。
混音控制
开始
恢复播放
@@ -163,4 +164,18 @@
录制倒计时
播放倒计时
开始麦克风录制
+ 美颜
+ 虚拟节拍器
+ Beats per Measure
+ Beats per Minute
+ 美颜
+ 美白
+ 红润
+ 锐利
+ 平滑
+ 暗光增强
+ 色彩增强
+ 视频降噪
+ 强度
+ 肤色保护
\ No newline at end of file
diff --git a/Android/APIExample/app/src/main/res/values/strings.xml b/Android/APIExample/app/src/main/res/values/strings.xml
index 3f82e0f55..c7813b111 100644
--- a/Android/APIExample/app/src/main/res/values/strings.xml
+++ b/Android/APIExample/app/src/main/res/values/strings.xml
@@ -136,6 +136,7 @@
This example shows how to use sendDataStream API to send your custom data along with Video Frame in Channel.
This example shows how to register Audio Observer to engine for extract raw audio data during RTC communication
Audio Loopback
+ This example shows the features for video enhancement for RTC communication.
Audio Mixing Control
Start
Resume
@@ -167,4 +168,18 @@
Recording Countdown
Playing Countdown
Recording on Microphone
+ Face Beautify
+ Rhythm Player
+ Beats per Measure
+ Beats per Minute
+ Face Beautify
+ Lightening
+ Redness
+ Sharpness
+ Lightening Enhance
+ Colorful Enhance
+ Smoothness
+ Video Denoise
+ Strength
+ Skin Protect
diff --git a/iOS/APIExample.xcodeproj/project.pbxproj b/iOS/APIExample.xcodeproj/project.pbxproj
index b43ef1bee..2b5a09425 100644
--- a/iOS/APIExample.xcodeproj/project.pbxproj
+++ b/iOS/APIExample.xcodeproj/project.pbxproj
@@ -113,6 +113,9 @@
8407E0942472320800AC5DE8 /* (null) in Sources */ = {isa = PBXBuildFile; };
8B349FE32681E2CE007247F2 /* agora-logo.png in Resources */ = {isa = PBXBuildFile; fileRef = 8B349FE22681E2CE007247F2 /* agora-logo.png */; };
8B98CAA42664914D001B5454 /* AgoraAudioProcessing.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8B98CAA32664914D001B5454 /* AgoraAudioProcessing.mm */; };
+ 8BAF3C992758693B0073B30E /* VideoProcess.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8BAF3C942758693A0073B30E /* VideoProcess.strings */; };
+ 8BAF3C9A2758693B0073B30E /* VideoProcess.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BAF3C962758693A0073B30E /* VideoProcess.swift */; };
+ 8BAF3C9B2758693B0073B30E /* VideoProcess.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8BAF3C972758693A0073B30E /* VideoProcess.storyboard */; };
A7847F922458062900469187 /* StatisticsInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7847F912458062900469187 /* StatisticsInfo.swift */; };
A7847F942458089E00469187 /* AgoraExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7847F932458089E00469187 /* AgoraExtension.swift */; };
A7BD7660247CC6920062A6B3 /* UITypeAlias.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7BD765F247CC6920062A6B3 /* UITypeAlias.swift */; };
@@ -316,6 +319,9 @@
8B98CAA12664914D001B5454 /* AgoraAudioProcessing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AgoraAudioProcessing.h; sourceTree = ""; };
8B98CAA22664914D001B5454 /* AgoraAudioCriticalSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AgoraAudioCriticalSection.h; sourceTree = ""; };
8B98CAA32664914D001B5454 /* AgoraAudioProcessing.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AgoraAudioProcessing.mm; sourceTree = ""; };
+ 8BAF3C952758693A0073B30E /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/VideoProcess.strings"; sourceTree = ""; };
+ 8BAF3C962758693A0073B30E /* VideoProcess.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoProcess.swift; sourceTree = ""; };
+ 8BAF3C982758693A0073B30E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/VideoProcess.storyboard; sourceTree = ""; };
92EACE913B50B28F1588FE03 /* Pods-Agora-ScreenShare-Extension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Agora-ScreenShare-Extension.release.xcconfig"; path = "Target Support Files/Pods-Agora-ScreenShare-Extension/Pods-Agora-ScreenShare-Extension.release.xcconfig"; sourceTree = ""; };
960FD7C836F90E68E6776106 /* Pods_APIExample_Mac.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_APIExample_Mac.framework; sourceTree = BUILT_PRODUCTS_DIR; };
A7847F912458062900469187 /* StatisticsInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatisticsInfo.swift; sourceTree = ""; };
@@ -809,6 +815,16 @@
path = RawAudioData;
sourceTree = "";
};
+ 8BAF3C932758693A0073B30E /* VideoProcess */ = {
+ isa = PBXGroup;
+ children = (
+ 8BAF3C942758693A0073B30E /* VideoProcess.strings */,
+ 8BAF3C962758693A0073B30E /* VideoProcess.swift */,
+ 8BAF3C972758693A0073B30E /* VideoProcess.storyboard */,
+ );
+ path = VideoProcess;
+ sourceTree = "";
+ };
A75A56D324A0603000D0089E /* Basic */ = {
isa = PBXGroup;
children = (
@@ -821,6 +837,7 @@
A75A56D724A0603000D0089E /* Advanced */ = {
isa = PBXGroup;
children = (
+ 8BAF3C932758693A0073B30E /* VideoProcess */,
5771635C264536750072DE96 /* CustomPcmAudioSource */,
576EA57825ADC482000B3D79 /* VideoChat */,
57B7FC81259C305B00407BE1 /* RawAudioData */,
@@ -979,6 +996,7 @@
033A9F2A252D737900BC26E1 /* Localizable.strings in Resources */,
033A9F7F252D8B5900BC26E1 /* AudioMixing.storyboard in Resources */,
033A9F70252D8B3E00BC26E1 /* SuperResolution.storyboard in Resources */,
+ 8BAF3C9B2758693B0073B30E /* VideoProcess.storyboard in Resources */,
033A9F7A252D8B5000BC26E1 /* MediaPlayer.storyboard in Resources */,
033A9F35252D896100BC26E1 /* RawMediaData.storyboard in Resources */,
0339D6D224E91B80008739CD /* QuickSwitchChannelVCItem.xib in Resources */,
@@ -1001,6 +1019,7 @@
033A9F30252D860100BC26E1 /* JoinChannelAudio.storyboard in Resources */,
0364C1FD2551AD6D00C6C0AE /* ARKit.storyboard in Resources */,
033A9F75252D8B4800BC26E1 /* ScreenShare.storyboard in Resources */,
+ 8BAF3C992758693B0073B30E /* VideoProcess.strings in Resources */,
033A9F5C252D89FD00BC26E1 /* CustomVideoSourcePush.storyboard in Resources */,
03D13BD92448758B00B599B3 /* Assets.xcassets in Resources */,
033A9F6B252D8B3500BC26E1 /* MediaChannelRelay.storyboard in Resources */,
@@ -1124,6 +1143,7 @@
57716360264539FF0072DE96 /* AgoraPcmSourcePush.swift in Sources */,
7F76DCA92571794C00E8B7BC /* SettingsCells.swift in Sources */,
033A9EE5252D5C6900BC26E1 /* VideoMetadata.swift in Sources */,
+ 8BAF3C9A2758693B0073B30E /* VideoProcess.swift in Sources */,
036C42BF24D5853200A59000 /* AgoraMediaRawData.m in Sources */,
036C42B024D2955D00A59000 /* AgoraCameraSourcePush.swift in Sources */,
0318857924CD667A00C699EB /* SettingsViewController.swift in Sources */,
@@ -1418,6 +1438,22 @@
name = RawAudioData.storyboard;
sourceTree = "";
};
+ 8BAF3C942758693A0073B30E /* VideoProcess.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 8BAF3C952758693A0073B30E /* zh-Hans */,
+ );
+ name = VideoProcess.strings;
+ sourceTree = "";
+ };
+ 8BAF3C972758693A0073B30E /* VideoProcess.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 8BAF3C982758693A0073B30E /* Base */,
+ );
+ name = VideoProcess.storyboard;
+ sourceTree = "";
+ };
A7CA48C224553CF600507435 /* Popover.storyboard */ = {
isa = PBXVariantGroup;
children = (
@@ -1434,10 +1470,10 @@
baseConfigurationReference = FAAC2AEE355D103B9E8527B5 /* Pods-Agora-ScreenShare-Extension.debug.xcconfig */;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
- CODE_SIGN_IDENTITY = "Apple Development: Qianze Zhang (3C9KJFP729)";
- CODE_SIGN_STYLE = Manual;
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
- DEVELOPMENT_TEAM = GM72UGLGZW;
+ DEVELOPMENT_TEAM = YS397FG5PA;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/APIExample",
@@ -1456,7 +1492,7 @@
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "io.agora.api.example.Agora-ScreenShare-Extension";
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE_SPECIFIER = App;
+ PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
SWIFT_OBJC_BRIDGING_HEADER = "Agora-ScreenShare-Extension/Agora-ScreenShare-Extension-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -1470,10 +1506,10 @@
baseConfigurationReference = 92EACE913B50B28F1588FE03 /* Pods-Agora-ScreenShare-Extension.release.xcconfig */;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
- CODE_SIGN_IDENTITY = "Apple Development: Qianze Zhang (3C9KJFP729)";
- CODE_SIGN_STYLE = Manual;
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
- DEVELOPMENT_TEAM = GM72UGLGZW;
+ DEVELOPMENT_TEAM = YS397FG5PA;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/APIExample",
@@ -1492,7 +1528,7 @@
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "io.agora.api.example.Agora-ScreenShare-Extension";
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE_SPECIFIER = App;
+ PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
SWIFT_OBJC_BRIDGING_HEADER = "Agora-ScreenShare-Extension/Agora-ScreenShare-Extension-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@@ -1623,10 +1659,10 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
- CODE_SIGN_IDENTITY = "Apple Development: Qianze Zhang (3C9KJFP729)";
- CODE_SIGN_STYLE = Manual;
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
- DEVELOPMENT_TEAM = GM72UGLGZW;
+ DEVELOPMENT_TEAM = YS397FG5PA;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/APIExample",
@@ -1640,7 +1676,7 @@
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = io.agora.api.example;
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE_SPECIFIER = App;
+ PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "APIExample/APIExample-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@@ -1655,10 +1691,10 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
- CODE_SIGN_IDENTITY = "Apple Development: Qianze Zhang (3C9KJFP729)";
- CODE_SIGN_STYLE = Manual;
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
- DEVELOPMENT_TEAM = GM72UGLGZW;
+ DEVELOPMENT_TEAM = YS397FG5PA;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/APIExample",
@@ -1672,7 +1708,7 @@
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = io.agora.api.example;
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE_SPECIFIER = App;
+ PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "APIExample/APIExample-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
diff --git a/iOS/APIExample/Examples/Advanced/CustomPcmAudioSource/CustomPcmAudioSource.swift b/iOS/APIExample/Examples/Advanced/CustomPcmAudioSource/CustomPcmAudioSource.swift
index dcda2f7c1..4a133b31c 100644
--- a/iOS/APIExample/Examples/Advanced/CustomPcmAudioSource/CustomPcmAudioSource.swift
+++ b/iOS/APIExample/Examples/Advanced/CustomPcmAudioSource/CustomPcmAudioSource.swift
@@ -151,12 +151,11 @@ extension CustomPcmAudioSourceMain: AgoraPcmSourcePushDelegate {
func onAudioFrame(data: UnsafeMutablePointer, samples: UInt) {
let frame = AgoraAudioFrame()
frame.buffer = UnsafeMutableRawPointer(data)
- frame.samplesPerSec = 48000
- frame.channels = 2
- frame.bytesPerSample = 32 * 8
+ frame.samplesPerSec = Int(sampleRate)
+ frame.channels = Int(channel)
+ frame.bytesPerSample = 2
frame.samplesPerChannel = self.samples / 2
let ret = agoraKit.pushExternalAudioFrameRawData(pushPos, frame: frame)
- agoraKit.pushExternalAudioFrameRawData(data, samples: samples, timestamp: 0)
print("push result: \(ret)")
}
}
diff --git a/iOS/APIExample/Examples/Advanced/RTMPStreaming/RTMPStreaming.swift b/iOS/APIExample/Examples/Advanced/RTMPStreaming/RTMPStreaming.swift
index c49d6bbaa..7d48f0318 100644
--- a/iOS/APIExample/Examples/Advanced/RTMPStreaming/RTMPStreaming.swift
+++ b/iOS/APIExample/Examples/Advanced/RTMPStreaming/RTMPStreaming.swift
@@ -60,7 +60,7 @@ class RTMPStreamingMain: BaseViewController {
didSet {
rtmpTextField.isEnabled = !isPublished
transcodingSwitch.isEnabled = !isPublished
- publishButton.title = isPublished ? "stop" : "push"
+ publishButton.title = isPublished ? "stop".localized : "push".localized
}
}
@@ -164,25 +164,29 @@ class RTMPStreamingMain: BaseViewController {
if(isPublished) {
// stop rtmp streaming
unpublishing = true
- agoraKit.removePublishStreamUrl(rtmpURL)
+ agoraKit.stopRtmpStream(rtmpURL)
} else {
// resign rtmp text field
rtmpTextField.resignFirstResponder()
-
- // check whether we need to enable transcoding
- let transcodingEnabled = transcodingSwitch.isOn
- if(transcodingEnabled){
- // we will use transcoding to composite multiple hosts' video
- // therefore we have to create a livetranscoding object and call before addPublishStreamUrl
- transcoding.size = CGSize(width: CANVAS_WIDTH, height: CANVAS_HEIGHT)
- agoraKit.setLiveTranscoding(transcoding)
- }
- agoraKit.addPublishStreamUrl(rtmpURL, transcodingEnabled: transcodingEnabled)
+ startRtmpStreaming(isTranscoding: transcodingSwitch.isOn, rtmpURL: rtmpURL)
startRetryTimer()
self.rtmpURL = rtmpURL
}
}
+ func startRtmpStreaming(isTranscoding: Bool, rtmpURL: String) {
+ if(isTranscoding){
+ // we will use transcoding to composite multiple hosts' video
+ // therefore we have to create a livetranscoding object and call before addPublishStreamUrl
+ transcoding.size = CGSize(width: CANVAS_WIDTH, height: CANVAS_HEIGHT)
+ agoraKit.setLiveTranscoding(transcoding)
+ agoraKit.startRtmpStream(withTranscoding: rtmpURL, transcoding: transcoding)
+ }
+ else{
+ agoraKit.startRtmpStreamWithoutTranscoding(rtmpURL)
+ }
+ }
+
func startRetryTimer() {
// begin timer to update progress
if(timer == nil) {
@@ -192,7 +196,7 @@ class RTMPStreamingMain: BaseViewController {
return
}
let transcodingEnabled = weakself.transcodingSwitch.isOn
- weakself.agoraKit.addPublishStreamUrl(rtmpURL, transcodingEnabled: transcodingEnabled)
+ self?.startRtmpStreaming(isTranscoding: transcodingEnabled, rtmpURL: rtmpURL)
})
}
}
@@ -275,7 +279,7 @@ extension RTMPStreamingMain: AgoraRtcEngineDelegate {
user.uid = uid
self.transcoding.add(user)
// remember you need to call setLiveTranscoding again if you changed the layout
- agoraKit.setLiveTranscoding(transcoding)
+ agoraKit.updateRtmpTranscoding(transcoding)
}
}
@@ -305,7 +309,7 @@ extension RTMPStreamingMain: AgoraRtcEngineDelegate {
}
remoteUid = nil
// remember you need to call setLiveTranscoding again if you changed the layout
- agoraKit.setLiveTranscoding(transcoding)
+ agoraKit.updateRtmpTranscoding(transcoding)
}
}
@@ -315,62 +319,51 @@ extension RTMPStreamingMain: AgoraRtcEngineDelegate {
/// @param reason
func rtcEngine(_ engine: AgoraRtcEngineKit, rtmpStreamingChangedToState url: String, state: AgoraRtmpStreamingState, errorCode: AgoraRtmpStreamingErrorCode) {
LogUtils.log(message: "rtmp streaming: \(url) state \(state.rawValue) error \(errorCode.rawValue)", level: .info)
- if(state == .running) {
- self.showAlert(title: "Notice", message: "RTMP Publish Success")
- isPublished = true
- stopRetryTimer()
- } else if(state == .failure) {
- self.showAlert(title: "Error", message: "RTMP Publish Failed: \(errorCode.rawValue)")
- stopRetryTimer()
- } else if(state == .idle) {
- self.showAlert(title: "Notice", message: "RTMP Publish Stopped")
- isPublished = false
- }
- }
-
- func rtcEngine(_ engine: AgoraRtcEngineKit, rtmpStreamingEventWithUrl url: String, eventCode: AgoraRtmpStreamingEvent) {
- if(eventCode == .urlAlreadyInUse) {
- self.showAlert(title: "Error", message: "The URL is already in Use.")
+ guard let rtmpURL = rtmpTextField.text else {
+ return
}
- }
-
- func rtcEngine(_ engine: AgoraRtcEngineKit, streamPublishedWithUrl url: String, errorCode: AgoraErrorCode) {
- if(errorCode == AgoraErrorCode.noError){
- retried = 0
- stopRetryTimer()
- } else {
- switch errorCode {
- case .failed ,.timedOut, .publishStreamInternalServerError:
- engine.removePublishStreamUrl(url)
- break
- case .publishStreamNotFound:
- if retried >= MAX_RETRY_TIMES {
- return
- }
- guard let rtmpURL = rtmpTextField.text else {
- return
- }
- let transcodingEnabled = transcodingSwitch.isOn
- agoraKit.addPublishStreamUrl(rtmpURL, transcodingEnabled: transcodingEnabled)
+ if state == .running {
+ if errorCode == .streamingErrorCodeOK {
+ self.showAlert(title: "Notice", message: "RTMP Publish Success")
+ isPublished = true
+ stopRetryTimer()
+ retried = 0
+ }
+ } else if state == .failure {
+ agoraKit.stopRtmpStream(rtmpURL)
+ if errorCode == .streamingErrorCodeInternalServerError
+ || errorCode == .streamingErrorCodeStreamNotFound
+ || errorCode == .streamPublishErrorNetDown
+ || errorCode == .streamingErrorCodeRtmpServerError
+ || errorCode == .streamingErrorCodeConnectionTimeout {
+ self.showAlert(title: "Error", message: "RTMP Publish Failed: \(errorCode.rawValue)")
+ }
+ else{
+ stopRetryTimer()
+ unpublishing = true
+ }
+ } else if state == .idle {
+ if unpublishing {
+ unpublishing = false
+ self.showAlert(title: "Notice", message: "RTMP Publish Stopped")
+ isPublished = false
+ }
+ else if retried >= MAX_RETRY_TIMES{
+ stopRetryTimer()
+ retried = 0
+ self.showAlert(title: "Notice", message: "RTMP Publish Stopped")
+ isPublished = false
+ }
+ else {
retried += 1
- default:
- print("unhandled rtmp streaming error: \(errorCode.rawValue)")
+ startRtmpStreaming(isTranscoding: transcodingSwitch.isOn, rtmpURL: rtmpURL)
}
}
}
- func rtcEngine(_ engine: AgoraRtcEngineKit, streamUnpublishedWithUrl url: String) {
- if unpublishing || retried >= MAX_RETRY_TIMES {
- return
- }
- guard let rtmpURL = rtmpTextField.text else {
- return
- }
- let transcodingEnabled = transcodingSwitch.isOn
- agoraKit.addPublishStreamUrl(rtmpURL, transcodingEnabled: transcodingEnabled)
- retried += 1
- if(unpublishing){
- unpublishing = false;
+ func rtcEngine(_ engine: AgoraRtcEngineKit, rtmpStreamingEventWithUrl url: String, eventCode: AgoraRtmpStreamingEvent) {
+ if(eventCode == .urlAlreadyInUse) {
+ self.showAlert(title: "Error", message: "The URL is already in Use.")
}
}
diff --git a/iOS/APIExample/Examples/Advanced/VideoProcess/Base.lproj/VideoProcess.storyboard b/iOS/APIExample/Examples/Advanced/VideoProcess/Base.lproj/VideoProcess.storyboard
new file mode 100644
index 000000000..7575139b5
--- /dev/null
+++ b/iOS/APIExample/Examples/Advanced/VideoProcess/Base.lproj/VideoProcess.storyboard
@@ -0,0 +1,220 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOS/APIExample/Examples/Advanced/VideoProcess/VideoProcess.swift b/iOS/APIExample/Examples/Advanced/VideoProcess/VideoProcess.swift
new file mode 100644
index 000000000..6ceea24c7
--- /dev/null
+++ b/iOS/APIExample/Examples/Advanced/VideoProcess/VideoProcess.swift
@@ -0,0 +1,252 @@
+//
+// VideoProcess.swift
+// APIExample
+//
+// Created by xianing on 2021/11/12.
+// Copyright © 2021 Agora Corp. All rights reserved.
+//
+
+import UIKit
+import AgoraRtcKit
+import AGEVideoLayout
+
+class VideoProcessEntry : UIViewController
+{
+ @IBOutlet weak var joinButton: AGButton!
+ @IBOutlet weak var channelTextField: AGTextField!
+ let identifier = "VideoProcess"
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ }
+
+ @IBAction func doJoinPressed(sender: AGButton) {
+ guard let channelName = channelTextField.text else {return}
+ //resign channel text field
+ channelTextField.resignFirstResponder()
+
+ let storyBoard: UIStoryboard = UIStoryboard(name: identifier, bundle: nil)
+ // create new view controller every time to ensure we get a clean vc
+ guard let newViewController = storyBoard.instantiateViewController(withIdentifier: identifier) as? BaseViewController else {return}
+ newViewController.title = channelName
+ newViewController.configs = ["channelName":channelName]
+ self.navigationController?.pushViewController(newViewController, animated: true)
+ }
+}
+
+class VideoProcessMain : BaseViewController
+{
+ var localVideo = Bundle.loadVideoView(type: .local, audioOnly: false)
+ var remoteVideo = Bundle.loadVideoView(type: .remote, audioOnly: false)
+
+ @IBOutlet weak var container: AGEVideoContainer!
+ var agoraKit: AgoraRtcEngineKit!
+
+ @IBOutlet weak var beauty: UISwitch!
+ @IBOutlet weak var lightness: UISwitch!
+ @IBOutlet weak var colorful: UISwitch!
+ @IBOutlet weak var denoiser: UISwitch!
+
+ @IBOutlet weak var lightenSlider: UISlider!
+ @IBOutlet weak var rednessSlider: UISlider!
+ @IBOutlet weak var sharpnessSlider: UISlider!
+ @IBOutlet weak var smoothSlider: UISlider!
+
+ // indicate if current instance has joined channel
+ var isJoined: Bool = false
+ var beautifyOption = AgoraBeautyOptions()
+ var skinProtect = 1.0
+ var strength = 0.5
+
+ override func viewDidLoad(){
+ super.viewDidLoad()
+ // layout render view
+ localVideo.setPlaceholder(text: "Local Host".localized)
+ remoteVideo.setPlaceholder(text: "Remote Host".localized)
+ container.layoutStream(views: [localVideo, remoteVideo])
+
+ // set up agora instance when view loaded
+ let config = AgoraRtcEngineConfig()
+ config.appId = KeyCenter.AppId
+ config.areaCode = GlobalSettings.shared.area.rawValue
+
+ // setup log file path
+ let logConfig = AgoraLogConfig()
+ logConfig.level = .info
+ config.logConfig = logConfig
+
+ agoraKit = AgoraRtcEngineKit.sharedEngine(with: config, delegate: self)
+
+ // get channel name from configs
+ guard let channelName = configs["channelName"] as? String,
+ let resolution = GlobalSettings.shared.getSetting(key: "resolution")?.selectedOption().value as? CGSize,
+ let fps = GlobalSettings.shared.getSetting(key: "fps")?.selectedOption().value as? AgoraVideoFrameRate,
+ let orientation = GlobalSettings.shared.getSetting(key: "orientation")?.selectedOption().value as? AgoraVideoOutputOrientationMode else {return}
+
+ // make myself a broadcaster
+ agoraKit.setChannelProfile(.liveBroadcasting)
+ agoraKit.setClientRole(.broadcaster)
+
+ // enable video module and set up video encoding configs
+ agoraKit.enableVideo()
+ agoraKit.setVideoEncoderConfiguration(AgoraVideoEncoderConfiguration(size: resolution,
+ frameRate: fps,
+ bitrate: AgoraVideoBitrateStandard,
+ orientationMode: orientation))
+
+ // set up local video to render your local camera preview
+ let videoCanvas = AgoraRtcVideoCanvas()
+ videoCanvas.uid = 0
+ // the view to be binded
+ videoCanvas.view = localVideo.videoView
+ videoCanvas.renderMode = .hidden
+ agoraKit.setupLocalVideo(videoCanvas)
+
+ // Set audio route to speaker
+ agoraKit.setDefaultAudioRouteToSpeakerphone(true)
+
+ // start joining channel
+ // 1. Users can only see each other after they join the
+ // same channel successfully using the same app id.
+ // 2. If app certificate is turned on at dashboard, token is needed
+ // when joining channel. The channel name and uid used to calculate
+ // the token has to match the ones used for channel join
+ let option = AgoraRtcChannelMediaOptions()
+ let result = agoraKit.joinChannel(byToken: KeyCenter.Token, channelId: channelName, info: nil, uid: 0, options: option)
+ if result != 0 {
+ // Usually happens with invalid parameters
+ // Error code description can be found at:
+ // en: https://docs.agora.io/en/Voice/API%20Reference/oc/Constants/AgoraErrorCode.html
+ // cn: https://docs.agora.io/cn/Voice/API%20Reference/oc/Constants/AgoraErrorCode.html
+ self.showAlert(title: "Error", message: "joinChannel call failed: \(result), please check your params")
+ }
+ }
+
+ override func willMove(toParent parent: UIViewController?) {
+ if parent == nil {
+ // leave channel when exiting the view
+ if isJoined {
+ agoraKit.leaveChannel { (stats) -> Void in
+ LogUtils.log(message: "left channel, duration: \(stats.duration)", level: .info)
+ }
+ }
+ }
+ }
+
+ @IBAction func onLightenSlider(_ sender:UISlider){
+ beautifyOption.lighteningLevel = sender.value
+ agoraKit.setBeautyEffectOptions(beauty.isOn, options: beautifyOption)
+ }
+ @IBAction func onRednessSlider(_ sender:UISlider){
+ beautifyOption.rednessLevel = sender.value
+ agoraKit.setBeautyEffectOptions(beauty.isOn, options: beautifyOption)
+ }
+ @IBAction func onSharpnessSlider(_ sender:UISlider){
+ beautifyOption.sharpnessLevel = sender.value
+ agoraKit.setBeautyEffectOptions(beauty.isOn, options: beautifyOption)
+ }
+ @IBAction func onSmoothSlider(_ sender:UISlider){
+ beautifyOption.smoothnessLevel = sender.value
+ agoraKit.setBeautyEffectOptions(beauty.isOn, options: beautifyOption)
+ }
+ @IBAction func onChangeBeauty(_ sender:UISwitch){
+ agoraKit.setBeautyEffectOptions(sender.isOn, options: beautifyOption)
+ }
+}
+
+extension VideoProcessMain: AgoraRtcEngineDelegate {
+ /// callback when warning occured for agora sdk, warning can usually be ignored, still it's nice to check out
+ /// what is happening
+ /// Warning code description can be found at:
+ /// en: https://docs.agora.io/en/Voice/API%20Reference/oc/Constants/AgoraWarningCode.html
+ /// cn: https://docs.agora.io/cn/Voice/API%20Reference/oc/Constants/AgoraWarningCode.html
+ /// @param warningCode warning code of the problem
+ func rtcEngine(_ engine: AgoraRtcEngineKit, didOccurWarning warningCode: AgoraWarningCode) {
+ LogUtils.log(message: "warning: \(warningCode.description)", level: .warning)
+ }
+
+ /// callback when error occured for agora sdk, you are recommended to display the error descriptions on demand
+ /// to let user know something wrong is happening
+ /// Error code description can be found at:
+ /// en: https://docs.agora.io/en/Voice/API%20Reference/oc/Constants/AgoraErrorCode.html
+ /// cn: https://docs.agora.io/cn/Voice/API%20Reference/oc/Constants/AgoraErrorCode.html
+ /// @param errorCode error code of the problem
+ func rtcEngine(_ engine: AgoraRtcEngineKit, didOccurError errorCode: AgoraErrorCode) {
+ LogUtils.log(message: "error: \(errorCode)", level: .error)
+ self.showAlert(title: "Error", message: "Error \(errorCode.description) occur")
+ }
+
+ /// callback when the local user joins a specified channel.
+ /// @param channel
+ /// @param uid uid of local user
+ /// @param elapsed time elapse since current sdk instance join the channel in ms
+ func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinChannel channel: String, withUid uid: UInt, elapsed: Int) {
+ self.isJoined = true
+ LogUtils.log(message: "Join \(channel) with uid \(uid) elapsed \(elapsed)ms", level: .info)
+ }
+
+ /// callback when a remote user is joinning the channel, note audience in live broadcast mode will NOT trigger this event
+ /// @param uid uid of remote joined user
+ /// @param elapsed time elapse since current sdk instance join the channel in ms
+ func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int) {
+ LogUtils.log(message: "remote user join: \(uid) \(elapsed)ms", level: .info)
+
+ // Only one remote video view is available for this
+ // tutorial. Here we check if there exists a surface
+ // view tagged as this uid.
+ let videoCanvas = AgoraRtcVideoCanvas()
+ videoCanvas.uid = uid
+ // the view to be binded
+ videoCanvas.view = remoteVideo.videoView
+ videoCanvas.renderMode = .hidden
+ agoraKit.setupRemoteVideo(videoCanvas)
+ }
+
+ /// callback when a remote user is leaving the channel, note audience in live broadcast mode will NOT trigger this event
+ /// @param uid uid of remote joined user
+ /// @param reason reason why this user left, note this event may be triggered when the remote user
+ /// become an audience in live broadcasting profile
+ func rtcEngine(_ engine: AgoraRtcEngineKit, didOfflineOfUid uid: UInt, reason: AgoraUserOfflineReason) {
+ LogUtils.log(message: "remote user left: \(uid) reason \(reason)", level: .info)
+
+ // to unlink your view from sdk, so that your view reference will be released
+ // note the video will stay at its last frame, to completely remove it
+ // you will need to remove the EAGL sublayer from your binded view
+ let videoCanvas = AgoraRtcVideoCanvas()
+ videoCanvas.uid = uid
+ // the view to be binded
+ videoCanvas.view = nil
+ videoCanvas.renderMode = .hidden
+ agoraKit.setupRemoteVideo(videoCanvas)
+ }
+
+ /// Reports the statistics of the current call. The SDK triggers this callback once every two seconds after the user joins the channel.
+ /// @param stats stats struct
+ func rtcEngine(_ engine: AgoraRtcEngineKit, reportRtcStats stats: AgoraChannelStats) {
+ localVideo.statsInfo?.updateChannelStats(stats)
+ }
+
+ /// Reports the statistics of the uploading local video streams once every two seconds.
+ /// @param stats stats struct
+ func rtcEngine(_ engine: AgoraRtcEngineKit, localVideoStats stats: AgoraRtcLocalVideoStats) {
+ localVideo.statsInfo?.updateLocalVideoStats(stats)
+ }
+
+ /// Reports the statistics of the uploading local audio streams once every two seconds.
+ /// @param stats stats struct
+ func rtcEngine(_ engine: AgoraRtcEngineKit, localAudioStats stats: AgoraRtcLocalAudioStats) {
+ localVideo.statsInfo?.updateLocalAudioStats(stats)
+ }
+
+ /// Reports the statistics of the video stream from each remote user/host.
+ /// @param stats stats struct
+ func rtcEngine(_ engine: AgoraRtcEngineKit, remoteVideoStats stats: AgoraRtcRemoteVideoStats) {
+ remoteVideo.statsInfo?.updateVideoStats(stats)
+ }
+
+ /// Reports the statistics of the audio stream from each remote user/host.
+ /// @param stats stats struct for current call statistics
+ func rtcEngine(_ engine: AgoraRtcEngineKit, remoteAudioStats stats: AgoraRtcRemoteAudioStats) {
+ remoteVideo.statsInfo?.updateAudioStats(stats)
+ }
+}
diff --git a/iOS/APIExample/Examples/Advanced/VideoProcess/zh-Hans.lproj/VideoProcess.strings b/iOS/APIExample/Examples/Advanced/VideoProcess/zh-Hans.lproj/VideoProcess.strings
new file mode 100644
index 000000000..fc209274c
--- /dev/null
+++ b/iOS/APIExample/Examples/Advanced/VideoProcess/zh-Hans.lproj/VideoProcess.strings
@@ -0,0 +1,51 @@
+
+/* Class = "UISwitch"; title = "Face Beautify"; ObjectID = "0lt-yn-0yl"; */
+"0lt-yn-0yl.title" = "美颜";
+
+/* Class = "UIButton"; normalTitle = "Join"; ObjectID = "1Vr-cE-fQI"; */
+"1Vr-cE-fQI.normalTitle" = "加入";
+
+/* Class = "UILabel"; text = "Lightening Enhance"; ObjectID = "2y0-8E-3Er"; */
+"2y0-8E-3Er.text" = "暗光增强";
+
+/* Class = "UILabel"; text = "Smoothness"; ObjectID = "3Cr-C9-PWm"; */
+"3Cr-C9-PWm.text" = "平滑";
+
+/* Class = "UISwitch"; title = "Face Beautify"; ObjectID = "3jT-PF-fry"; */
+"3jT-PF-fry.title" = "美颜";
+
+/* Class = "UILabel"; text = "Lightening"; ObjectID = "6Yy-cm-VP3"; */
+"6Yy-cm-VP3.text" = "美白";
+
+/* Class = "UIViewController"; title = "Simple Filter"; ObjectID = "8Zn-pj-gkm"; */
+"8Zn-pj-gkm.title" = "视频增强";
+
+/* Class = "UISwitch"; title = "Face Beautify"; ObjectID = "HJK-u9-Pbi"; */
+"HJK-u9-Pbi.title" = "美颜";
+
+/* Class = "UILabel"; text = "Video Denoiser"; ObjectID = "IK4-hW-gbx"; */
+"IK4-hW-gbx.text" = "视频降噪";
+
+/* Class = "UILabel"; text = "Colorful Enhance"; ObjectID = "Ijy-ys-cZf"; */
+"Ijy-ys-cZf.text" = "色彩增强";
+
+/* Class = "UILabel"; text = "Face Beautify"; ObjectID = "Ty6-YP-tus"; */
+"Ty6-YP-tus.text" = "美颜";
+
+/* Class = "UISwitch"; title = "Face Beautify"; ObjectID = "YBf-iq-ZSD"; */
+"YBf-iq-ZSD.title" = "美颜";
+
+/* Class = "UILabel"; text = "Redness"; ObjectID = "c7I-oU-1Ty"; */
+"c7I-oU-1Ty.text" = "红润";
+
+/* Class = "UITextField"; placeholder = "Enter channel name"; ObjectID = "moa-hL-hhA"; */
+"moa-hL-hhA.placeholder" = "请输入频道名";
+
+/* Class = "UILabel"; text = "Sharpness"; ObjectID = "nxu-z3-DfB"; */
+"nxu-z3-DfB.text" = "锐利";
+
+/* Class = "UILabel"; text = "Strength"; ObjectID = "OIO-FL-y7u"; */
+"OIO-FL-y7u.text" = "强度";
+
+/* Class = "UILabel"; text = "Skin Protect"; ObjectID = "C1K-xQ-TKU"; */
+"C1K-xQ-TKU.text" = "肤色保护";
diff --git a/iOS/APIExample/ViewController.swift b/iOS/APIExample/ViewController.swift
index a578aa2e0..6c8e7d55e 100644
--- a/iOS/APIExample/ViewController.swift
+++ b/iOS/APIExample/ViewController.swift
@@ -47,6 +47,7 @@ class ViewController: AGViewController {
MenuItem(name: "Audio Mixing".localized, storyboard: "AudioMixing", controller: ""),
MenuItem(name: "Precall Test".localized, storyboard: "PrecallTest", controller: ""),
MenuItem(name: "Screen Share".localized, storyboard: "ScreenShare", controller: ""),
+ MenuItem(name: "Video Process".localized, storyboard: "VideoProcess", controller: "VideoProcess"),
MenuItem(name: "Super Resolution".localized, storyboard: "SuperResolution", controller: ""),
MenuItem(name: "Media Channel Relay".localized, storyboard: "MediaChannelRelay", controller: ""),
MenuItem(name: "Media Player".localized, storyboard: "MediaPlayer", controller: "MediaPlayer"),
diff --git a/iOS/APIExample/zh-Hans.lproj/Localizable.strings b/iOS/APIExample/zh-Hans.lproj/Localizable.strings
index 53e35c092..d94826531 100644
--- a/iOS/APIExample/zh-Hans.lproj/Localizable.strings
+++ b/iOS/APIExample/zh-Hans.lproj/Localizable.strings
@@ -117,7 +117,10 @@
"Sweet" = "甜美";
"Solid" = "稳重";
"Bass" = "重低音";
+"Video Process" = "美颜";
"Start Audio Test" = "开始音频测试";
"Stop Audio Test" = "停止音频测试";
"Start Video/Audio Test" = "开始音视频测试";
"Stop Video/Audio Test" = "停止音视频测试";
+"push" = "推流";
+"stop" = "停止";
diff --git a/macOS/APIExample.xcodeproj/project.pbxproj b/macOS/APIExample.xcodeproj/project.pbxproj
index c5a5b45c5..691d22a03 100644
--- a/macOS/APIExample.xcodeproj/project.pbxproj
+++ b/macOS/APIExample.xcodeproj/project.pbxproj
@@ -43,7 +43,6 @@
033A9FF5252EB5F400BC26E1 /* JoinMultiChannel.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 033A9FF7252EB5F400BC26E1 /* JoinMultiChannel.storyboard */; };
033A9FFA252EB5FD00BC26E1 /* ScreenShare.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 033A9FFC252EB5FD00BC26E1 /* ScreenShare.storyboard */; };
033A9FFF252EB60800BC26E1 /* StreamEncryption.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 033AA001252EB60800BC26E1 /* StreamEncryption.storyboard */; };
- 033AA005252EBBEC00BC26E1 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 033AA004252EBBEC00BC26E1 /* Localizable.strings */; };
034C626425257EA600296ECF /* GlobalSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 034C626325257EA600296ECF /* GlobalSettings.swift */; };
034C62672525857200296ECF /* JoinChannelAudio.swift in Sources */ = {isa = PBXBuildFile; fileRef = 034C62662525857200296ECF /* JoinChannelAudio.swift */; };
034C626C25259FC200296ECF /* JoinChannelVideo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 034C626B25259FC200296ECF /* JoinChannelVideo.swift */; };
@@ -88,6 +87,7 @@
57AF397B259B31AA00601E02 /* RawAudioData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57AF397A259B31AA00601E02 /* RawAudioData.swift */; };
57AF3981259B329B00601E02 /* RawAudioData.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 57AF3980259B329B00601E02 /* RawAudioData.storyboard */; };
596A9F79AF0CD8DC1CA93253 /* Pods_APIExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F65EF2B97B89DE4581B426B /* Pods_APIExample.framework */; };
+ 8B6F4EAD276ECC370097E67E /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8B6F4EAF276ECC370097E67E /* Localizable.strings */; };
8B733B8C267B1C0B00CC3DE3 /* bg.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8B733B8B267B1C0B00CC3DE3 /* bg.jpg */; };
EBDD0209B272C276B21B6270 /* Pods_APIExample_APIExampleUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FC2BAB0AC82140B7CEEA31DA /* Pods_APIExample_APIExampleUITests.framework */; };
/* End PBXBuildFile section */
@@ -173,7 +173,6 @@
033A9FFB252EB5FD00BC26E1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/ScreenShare.storyboard; sourceTree = ""; };
033AA000252EB60800BC26E1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/StreamEncryption.storyboard; sourceTree = ""; };
033AA003252EB60B00BC26E1 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/StreamEncryption.strings"; sourceTree = ""; };
- 033AA004252EBBEC00BC26E1 /* Localizable.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = ""; };
034C626325257EA600296ECF /* GlobalSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlobalSettings.swift; sourceTree = ""; };
034C62662525857200296ECF /* JoinChannelAudio.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JoinChannelAudio.swift; sourceTree = ""; };
034C626B25259FC200296ECF /* JoinChannelVideo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JoinChannelVideo.swift; sourceTree = ""; };
@@ -230,6 +229,7 @@
57AF3980259B329B00601E02 /* RawAudioData.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = RawAudioData.storyboard; sourceTree = ""; };
6F65EF2B97B89DE4581B426B /* Pods_APIExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_APIExample.framework; sourceTree = BUILT_PRODUCTS_DIR; };
84C863718A380DFD36ABF19F /* Pods-APIExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-APIExample.debug.xcconfig"; path = "Target Support Files/Pods-APIExample/Pods-APIExample.debug.xcconfig"; sourceTree = ""; };
+ 8B6F4EAE276ECC370097E67E /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; };
8B733B8B267B1C0B00CC3DE3 /* bg.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = bg.jpg; sourceTree = ""; };
B53F41CB5AC550EA43C47363 /* Pods-APIExampleTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-APIExampleTests.debug.xcconfig"; path = "Target Support Files/Pods-APIExampleTests/Pods-APIExampleTests.debug.xcconfig"; sourceTree = ""; };
B91A67063F1DBE9F621B114C /* Pods-APIExample-APIExampleUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-APIExample-APIExampleUITests.release.xcconfig"; path = "Target Support Files/Pods-APIExample-APIExampleUITests/Pods-APIExample-APIExampleUITests.release.xcconfig"; sourceTree = ""; };
@@ -520,7 +520,7 @@
03896D2E24F8A00F008593CD /* APIExample */ = {
isa = PBXGroup;
children = (
- 033AA004252EBBEC00BC26E1 /* Localizable.strings */,
+ 8B6F4EAF276ECC370097E67E /* Localizable.strings */,
034C629D25297ABB00296ECF /* Resources */,
03267E262500C779004A91A6 /* APIExample-Bridging-Header.h */,
0333E63824FA335C0063C5B0 /* Examples */,
@@ -777,7 +777,7 @@
033A9FFA252EB5FD00BC26E1 /* ScreenShare.storyboard in Resources */,
57645A03259B1C22007B1E30 /* CreateDataStream.strings in Resources */,
5770E2D5258C9E6F00812A80 /* Picker.xib in Resources */,
- 033AA005252EBBEC00BC26E1 /* Localizable.strings in Resources */,
+ 8B6F4EAD276ECC370097E67E /* Localizable.strings in Resources */,
57887A67258856B7006E962A /* Settings.storyboard in Resources */,
033A9FFF252EB60800BC26E1 /* StreamEncryption.storyboard in Resources */,
0301D31D2507C0F300DF3BEA /* MetalVideoView.xib in Resources */,
@@ -1182,6 +1182,14 @@
name = Settings.storyboard;
sourceTree = "";
};
+ 8B6F4EAF276ECC370097E67E /* Localizable.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 8B6F4EAE276ECC370097E67E /* zh-Hans */,
+ );
+ name = Localizable.strings;
+ sourceTree = "";
+ };
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
@@ -1306,10 +1314,10 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = APIExample/APIExample.entitlements;
- CODE_SIGN_IDENTITY = "Developer ID Application";
- CODE_SIGN_STYLE = Manual;
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
- DEVELOPMENT_TEAM = GM72UGLGZW;
+ DEVELOPMENT_TEAM = YS397FG5PA;
ENABLE_HARDENED_RUNTIME = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@@ -1322,7 +1330,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = io.agora.api.example.APIExample;
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE_SPECIFIER = apiexamplemac;
+ PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "APIExample/APIExample-Bridging-Header.h";
SWIFT_VERSION = 5.0;
};
@@ -1334,10 +1342,10 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = APIExample/APIExample.entitlements;
- CODE_SIGN_IDENTITY = "Developer ID Application";
- CODE_SIGN_STYLE = Manual;
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
- DEVELOPMENT_TEAM = GM72UGLGZW;
+ DEVELOPMENT_TEAM = YS397FG5PA;
ENABLE_HARDENED_RUNTIME = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@@ -1350,7 +1358,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = io.agora.api.example.APIExample;
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE_SPECIFIER = apiexamplemac;
+ PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "APIExample/APIExample-Bridging-Header.h";
SWIFT_VERSION = 5.0;
};
diff --git a/macOS/APIExample/Examples/Advanced/RTMPStreaming/RTMPStreaming.swift b/macOS/APIExample/Examples/Advanced/RTMPStreaming/RTMPStreaming.swift
index a814b1383..0fc2ece9a 100644
--- a/macOS/APIExample/Examples/Advanced/RTMPStreaming/RTMPStreaming.swift
+++ b/macOS/APIExample/Examples/Advanced/RTMPStreaming/RTMPStreaming.swift
@@ -20,7 +20,8 @@ class RTMPStreaming: BaseViewController {
var agoraKit: AgoraRtcEngineKit!
var transcoding = AgoraLiveTranscoding.default()
-
+ var unpublishing = false
+
/**
--- rtmpUrls Picker ---
*/
@@ -43,20 +44,22 @@ class RTMPStreaming: BaseViewController {
/// callback when remove streaming url button hit
@IBAction func onRemoveStreamingURL(_ sender: Any) {
guard let selectedURL = selectedrtmpUrl else { return }
- agoraKit.removePublishStreamUrl(selectedURL)
+ agoraKit.stopRtmpStream(selectedURL)
+ unpublishing = true
rtmpURLs.remove(at: selectRtmpUrlsPicker.indexOfSelectedItem)
selectRtmpUrlsPicker.picker.removeItem(at: selectRtmpUrlsPicker.indexOfSelectedItem)
}
-
+
/// callback when remove all streaming url button hit
@IBAction func onRemoveAllStreamingURL(_ sender: Any) {
for url in rtmpURLs {
- agoraKit.removePublishStreamUrl(url)
+ agoraKit.stopRtmpStream(url)
}
rtmpURLs = []
selectRtmpUrlsPicker.picker.removeAllItems()
+ unpublishing = true
}
-
+
/**
--- Channel TextField ---
*/
@@ -65,7 +68,7 @@ class RTMPStreaming: BaseViewController {
channelField.label.stringValue = "Channel".localized
channelField.field.placeholderString = "Channel Name".localized
}
-
+
/**
--- rtmp TextField ---
*/
@@ -89,21 +92,26 @@ class RTMPStreaming: BaseViewController {
showAlert(title: "Add Streaming URL Failed", message: "RTMP URL cannot be empty or not start with 'rtmp://'")
return
}
-
+
if transcodingEnabled {
// we will use transcoding to composite multiple hosts' video
// therefore we have to create a livetranscoding object and call before addPublishStreamUrl
transcoding.size = CGSize(width: CANVAS_WIDTH, height: CANVAS_HEIGHT)
- agoraKit.setLiveTranscoding(transcoding)
+ agoraKit.updateRtmpTranscoding(transcoding)
}
-
+
// start publishing to this URL
- agoraKit.addPublishStreamUrl(rtmpURL, transcodingEnabled: transcodingEnabled)
+ if transcodingEnabled {
+ agoraKit.startRtmpStream(withTranscoding: rtmpURL, transcoding: transcoding)
+ }
+ else {
+ agoraKit.startRtmpStreamWithoutTranscoding(rtmpURL)
+ }
// update properties and UI
rtmpURLs.append(rtmpURL)
selectRtmpUrlsPicker.picker.addItem(withTitle: rtmpURL)
}
-
+
/**
--- Button ---
*/
@@ -111,7 +119,7 @@ class RTMPStreaming: BaseViewController {
func initJoinChannelButton() {
joinChannelButton.title = isJoined ? "Leave Channel".localized : "Join Channel".localized
}
-
+
// indicate if current instance has joined channel
var isJoined: Bool = false {
didSet {
@@ -119,14 +127,14 @@ class RTMPStreaming: BaseViewController {
initJoinChannelButton()
}
}
-
+
// indicate for doing something
var isProcessing: Bool = false {
didSet {
joinChannelButton.isEnabled = !isProcessing
}
}
-
+
override func viewDidLoad() {
super.viewDidLoad()
layoutVideos(2)
@@ -136,13 +144,13 @@ class RTMPStreaming: BaseViewController {
config.areaCode = GlobalSettings.shared.area.rawValue
agoraKit = AgoraRtcEngineKit.sharedEngine(with: config, delegate: self)
agoraKit.enableVideo()
-
+
initSelectRtmpUrlsPicker()
initRtmpURLField()
initChannelField()
initJoinChannelButton()
}
-
+
override func viewWillBeRemovedFromSplitView() {
if isJoined {
agoraKit.leaveChannel { (stats:AgoraChannelStats) in
@@ -151,7 +159,7 @@ class RTMPStreaming: BaseViewController {
}
AgoraRtcEngineKit.destroy()
}
-
+
@IBAction func onJoinPressed(_ sender:Any) {
if !isJoined {
// check configuration
@@ -180,7 +188,7 @@ class RTMPStreaming: BaseViewController {
videoCanvas.view = localVideo.videocanvas
videoCanvas.renderMode = .hidden
agoraKit.setupLocalVideo(videoCanvas)
-
+
// start joining channel
// 1. Users can only see each other after they join the
// same channel successfully using the same app id.
@@ -240,7 +248,7 @@ extension RTMPStreaming: AgoraRtcEngineDelegate {
func rtcEngine(_ engine: AgoraRtcEngineKit, didOccurWarning warningCode: AgoraWarningCode) {
LogUtils.log(message: "warning: \(warningCode.rawValue)", level: .warning)
}
-
+
/// callback when error occured for agora sdk, you are recommended to display the error descriptions on demand
/// to let user know something wrong is happening
/// Error code description can be found at:
@@ -254,7 +262,7 @@ extension RTMPStreaming: AgoraRtcEngineDelegate {
}
self.showAlert(title: "Error", message: "Error \(errorCode.rawValue) occur")
}
-
+
/// callback when the local user joins a specified channel.
/// @param channel
/// @param uid uid of local user
@@ -265,7 +273,7 @@ extension RTMPStreaming: AgoraRtcEngineDelegate {
let localVideo = videos[0]
localVideo.uid = uid
LogUtils.log(message: "Join \(channel) with uid \(uid) elapsed \(elapsed)ms", level: .info)
-
+
// add transcoding user so the video stream will be involved
// in future RTMP Stream
let user = AgoraLiveTranscodingUser()
@@ -273,13 +281,13 @@ extension RTMPStreaming: AgoraRtcEngineDelegate {
user.uid = uid
transcoding.add(user)
}
-
+
/// callback when a remote user is joinning the channel, note audience in live broadcast mode will NOT trigger this event
/// @param uid uid of remote joined user
/// @param elapsed time elapse since current sdk instance join the channel in ms
func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int) {
LogUtils.log(message: "remote user join: \(uid) \(elapsed)ms", level: .info)
-
+
// find a VideoView w/o uid assigned
if let remoteVideo = videos.first(where: { $0.uid == nil }) {
let videoCanvas = AgoraRtcVideoCanvas()
@@ -292,7 +300,7 @@ extension RTMPStreaming: AgoraRtcEngineDelegate {
} else {
LogUtils.log(message: "no video canvas available for \(uid), cancel bind", level: .warning)
}
-
+
// update live transcoding
// add new user onto the canvas
let user = AgoraLiveTranscodingUser()
@@ -300,16 +308,16 @@ extension RTMPStreaming: AgoraRtcEngineDelegate {
user.uid = uid
self.transcoding.add(user)
// remember you need to call setLiveTranscoding again if you changed the layout
- agoraKit.setLiveTranscoding(transcoding)
+ agoraKit.updateRtmpTranscoding(transcoding)
}
-
+
/// callback when a remote user is leaving the channel, note audience in live broadcast mode will NOT trigger this event
/// @param uid uid of remote joined user
/// @param reason reason why this user left, note this event may be triggered when the remote user
/// become an audience in live broadcasting profile
func rtcEngine(_ engine: AgoraRtcEngineKit, didOfflineOfUid uid: UInt, reason: AgoraUserOfflineReason) {
LogUtils.log(message: "remote user left: \(uid) reason \(reason)", level: .info)
-
+
// to unlink your view from sdk, so that your view reference will be released
// note the video will stay at its last frame, to completely remove it
// you will need to remove the EAGL sublayer from your binded view
@@ -324,13 +332,13 @@ extension RTMPStreaming: AgoraRtcEngineDelegate {
} else {
LogUtils.log(message: "no matching video canvas for \(uid), cancel unbind", level: .warning)
}
-
+
// remove user from canvas if current cohost left channel
transcoding.removeUser(uid)
// remember you need to call setLiveTranscoding again if you changed the layout
- agoraKit.setLiveTranscoding(transcoding)
+ agoraKit.updateRtmpTranscoding(transcoding)
}
-
+
/// callback for state of rtmp streaming, for both good and bad state
/// @param url rtmp streaming url
/// @param state state of rtmp streaming
@@ -340,9 +348,31 @@ extension RTMPStreaming: AgoraRtcEngineDelegate {
if(state == .running) {
self.showAlert(title: "Notice", message: "\(url) Publish Success")
} else if(state == .failure) {
- self.showAlert(title: "Error", message: "\(url) Publish Failed: \(errorCode.rawValue)")
+ agoraKit.stopRtmpStream(url)
+ if errorCode == .streamingErrorCodeInternalServerError
+ || errorCode == .streamingErrorCodeStreamNotFound
+ || errorCode == .streamPublishErrorNetDown
+ || errorCode == .streamingErrorCodeRtmpServerError
+ || errorCode == .streamingErrorCodeConnectionTimeout {
+ self.showAlert(title: "Error", message: "\(url) Publish Failed: \(errorCode.rawValue)")
+ }
+ else{
+ unpublishing = true
+ }
} else if(state == .idle) {
self.showAlert(title: "Notice", message: "\(url) Publish Stopped")
+ if !unpublishing {
+ // start publishing to this URL
+ if transcodingEnabled {
+ agoraKit.startRtmpStream(withTranscoding: url, transcoding: transcoding)
+ }
+ else {
+ agoraKit.startRtmpStreamWithoutTranscoding(url)
+ }
+ }
+ else {
+ unpublishing = false
+ }
}
}
diff --git a/macOS/APIExample/Examples/Advanced/ScreenShare/ScreenShare.swift b/macOS/APIExample/Examples/Advanced/ScreenShare/ScreenShare.swift
index 644a30842..e1f677bba 100644
--- a/macOS/APIExample/Examples/Advanced/ScreenShare/ScreenShare.swift
+++ b/macOS/APIExample/Examples/Advanced/ScreenShare/ScreenShare.swift
@@ -204,6 +204,7 @@ class ScreenShare: BaseViewController {
params.frameRate = fps
params.dimensions = resolution.size()
let result = agoraKit.startScreenCapture(byDisplayId: UInt(screen.sourceId), rectangle: .zero, parameters: params)
+
if result != 0 {
// Usually happens with invalid parameters
// Error code description can be found at:
diff --git a/macOS/APIExample/SettingsController.swift b/macOS/APIExample/SettingsController.swift
index 643a0d099..86e4f6f01 100644
--- a/macOS/APIExample/SettingsController.swift
+++ b/macOS/APIExample/SettingsController.swift
@@ -33,10 +33,6 @@ class SettingsController: BaseViewController {
self.fpsPicker.addItems(withTitles: GlobalSettings.shared.fpsSetting.options.map { $0.label })
self.fpsPicker.selectItem(at: GlobalSettings.shared.fpsSetting.selected)
- self.proxyLabel.cell?.title = "Enable Cloud Proxy".localized
- self.proxyPicker.addItems(withTitles: GlobalSettings.shared.proxySetting.options.map { $0.label })
- self.proxyPicker.selectItem(at: GlobalSettings.shared.proxySetting.selected)
-
self.sdkVersion.cell?.title = "v\(AgoraRtcEngineKit.getSdkVersion())"
}
diff --git a/macOS/APIExample/ViewController.swift b/macOS/APIExample/ViewController.swift
index b083a09d5..d57fbdea3 100644
--- a/macOS/APIExample/ViewController.swift
+++ b/macOS/APIExample/ViewController.swift
@@ -37,9 +37,9 @@ class MenuController: NSViewController {
MenuItem(name: "Media Channel Relay".localized, identifier: "menuCell", controller: "ChannelMediaRelay", storyboard: "ChannelMediaRelay"),
MenuItem(name: "Audio Mixing".localized, identifier: "menuCell", controller: "AudioMixing", storyboard: "AudioMixing"),
MenuItem(name: "Voice Changer".localized, identifier: "menuCell", controller: "VoiceChanger", storyboard: "VoiceChanger"),
- MenuItem(name: "Precall Test".localized, identifier: "menuCell", controller: "PrecallTest", storyboard: "PrecallTest"),
MenuItem(name: "Create Data Stream".localized, identifier: "menuCell", controller: "CreateDataStream", storyboard: "CreateDataStream"),
- MenuItem(name: "Raw Audio Data".localized, identifier: "menuCell", controller: "RawAudioData", storyboard: "RawAudioData")
+ MenuItem(name: "Raw Audio Data".localized, identifier: "menuCell", controller: "RawAudioData", storyboard: "RawAudioData"),
+ MenuItem(name: "Precall Test".localized, identifier: "menuCell", controller: "PrecallTest", storyboard: "PrecallTest")
]
@IBOutlet weak var tableView:NSTableView!
@@ -109,9 +109,11 @@ extension MenuController: NSTableViewDataSource, NSTableViewDelegate {
func tableViewSelectionDidChange(_ notification: Notification) {
if tableView.selectedRow >= 0 && tableView.selectedRow < menus.count {
+ Thread.sleep(forTimeInterval: 1)
loadSplitViewItem(item: menus[tableView.selectedRow])
}
}
+
}
class ViewController: NSViewController {
@@ -128,4 +130,3 @@ class ViewController: NSViewController {
}
}
}
-
diff --git a/macOS/APIExample/Localizable.strings b/macOS/APIExample/zh-Hans.lproj/Localizable.strings
similarity index 100%
rename from macOS/APIExample/Localizable.strings
rename to macOS/APIExample/zh-Hans.lproj/Localizable.strings
diff --git a/macOS/Podfile b/macOS/Podfile
index 4b8def9bb..c75c54009 100644
--- a/macOS/Podfile
+++ b/macOS/Podfile
@@ -7,7 +7,7 @@ target 'APIExample' do
# Pods for APIExample
pod 'AGEVideoLayout', '~> 1.0.2'
- pod 'AgoraRtcEngine_macOS', '3.5.2'
+ pod 'AgoraRtcEngine_macOS', '3.6.0.1'
target 'APIExampleTests' do
inherit! :search_paths
diff --git a/windows/APIExample/APIExample/Advanced/MultiChannel/CAgoraMultiChannelDlg.h b/windows/APIExample/APIExample/Advanced/MultiChannel/CAgoraMultiChannelDlg.h
index 672597a86..6c3f15f93 100644
--- a/windows/APIExample/APIExample/Advanced/MultiChannel/CAgoraMultiChannelDlg.h
+++ b/windows/APIExample/APIExample/Advanced/MultiChannel/CAgoraMultiChannelDlg.h
@@ -455,7 +455,7 @@ class ChannelEventHandler :public agora::rtc::IChannelEventHandler
@param state The RTMP streaming state. See: #RTMP_STREAM_PUBLISH_STATE.
@param errCode The detailed error information for streaming. See: #RTMP_STREAM_PUBLISH_ERROR.
*/
- virtual void onRtmpStreamingStateChanged(IChannel *rtcChannel, const char *url, RTMP_STREAM_PUBLISH_STATE state, RTMP_STREAM_PUBLISH_ERROR errCode) {
+ virtual void onRtmpStreamingStateChanged(IChannel *rtcChannel, const char *url, RTMP_STREAM_PUBLISH_STATE state, RTMP_STREAM_PUBLISH_ERROR_TYPE errCode) {
}
diff --git a/windows/APIExample/APIExample/Advanced/RTMPStream/AgoraRtmpStreaming.cpp b/windows/APIExample/APIExample/Advanced/RTMPStream/AgoraRtmpStreaming.cpp
index 3fde38325..46cb6ed7d 100644
--- a/windows/APIExample/APIExample/Advanced/RTMPStream/AgoraRtmpStreaming.cpp
+++ b/windows/APIExample/APIExample/Advanced/RTMPStream/AgoraRtmpStreaming.cpp
@@ -89,7 +89,7 @@ void CAgoraRtmpStreamingDlgRtcEngineEventHandler::onUserOffline(uid_t uid, USER_
@param state The RTMP streaming state. See: #RTMP_STREAM_PUBLISH_STATE.
@param errCode The detailed error information for streaming. See: #RTMP_STREAM_PUBLISH_ERROR.
*/
-void CAgoraRtmpStreamingDlgRtcEngineEventHandler::onRtmpStreamingStateChanged(const char *url, RTMP_STREAM_PUBLISH_STATE state, RTMP_STREAM_PUBLISH_ERROR errCode)
+void CAgoraRtmpStreamingDlgRtcEngineEventHandler::onRtmpStreamingStateChanged(const char *url, RTMP_STREAM_PUBLISH_STATE state, RTMP_STREAM_PUBLISH_ERROR_TYPE errCode)
{
if (m_hMsgHanlder) {
PRtmpStreamStreamStateChanged rtmpState = new RtmpStreamStreamStateChanged;
@@ -116,31 +116,6 @@ void CAgoraRtmpStreamingDlgRtcEngineEventHandler::onRtmpStreamingEvent(const cha
}
}
-
-
-void CAgoraRtmpStreamingDlgRtcEngineEventHandler::onStreamUnpublished(const char *url)
-{
- if (m_hMsgHanlder) {
- PStreamPublished streamPublished = new StreamPublished;
- int len = strlen(url);
- char* publishUrl = new char[len + 1];
- memset(publishUrl, 0, sizeof(publishUrl));
- strcpy_s(publishUrl, len, url);
- ::PostMessage(m_hMsgHanlder, WM_MSGID(EID_RTMP_STREAM_STATE_UNPUBLISHED), (WPARAM)streamPublished, 0);
- }
-}
-
-void CAgoraRtmpStreamingDlgRtcEngineEventHandler::onStreamPublished(const char *url, int error)
-{
- if (m_hMsgHanlder) {
- int len = strlen(url);
- char* publishUrl = new char[len + 1];
- memset(publishUrl, 0, sizeof(publishUrl));
- strcpy_s(publishUrl, len, url);
- ::PostMessage(m_hMsgHanlder, WM_MSGID(EID_RTMP_STREAM_STATE_PUBLISHED), (WPARAM)publishUrl, error);
- }
-}
-
IMPLEMENT_DYNAMIC(CAgoraRtmpStreamingDlg, CDialogEx)
CAgoraRtmpStreamingDlg::CAgoraRtmpStreamingDlg(CWnd* pParent /*=nullptr*/)
@@ -182,8 +157,6 @@ BEGIN_MESSAGE_MAP(CAgoraRtmpStreamingDlg, CDialogEx)
ON_MESSAGE(WM_MSGID(EID_RTMP_STREAM_STATE_CHANGED), &CAgoraRtmpStreamingDlg::OnEIDRtmpStateChanged)
ON_MESSAGE(WM_MSGID(EID_USER_JOINED), &CAgoraRtmpStreamingDlg::OnEIDUserJoined)
ON_MESSAGE(WM_MSGID(EID_USER_OFFLINE), &CAgoraRtmpStreamingDlg::OnEIDUserOffline)
- ON_MESSAGE(WM_MSGID(EID_RTMP_STREAM_STATE_PUBLISHED), &CAgoraRtmpStreamingDlg::OnEIDStreamPublished)
- ON_MESSAGE(WM_MSGID(EID_RTMP_STREAM_STATE_UNPUBLISHED), &CAgoraRtmpStreamingDlg::OnEIDStreamUnpublished)
ON_BN_CLICKED(IDC_BUTTON_JOINCHANNEL, &CAgoraRtmpStreamingDlg::OnBnClickedButtonJoinchannel)
ON_BN_CLICKED(IDC_BUTTON_ADDSTREAM, &CAgoraRtmpStreamingDlg::OnBnClickedButtonAddstream)
ON_BN_CLICKED(IDC_BUTTON_REMOVE_STREAM, &CAgoraRtmpStreamingDlg::OnBnClickedButtonRemoveStream)
@@ -316,7 +289,7 @@ void CAgoraRtmpStreamingDlg::RemoveAllRtmpUrls()
for (int i = 0; i < m_cmbRtmpUrl.GetCount(); ++i) {
m_cmbRtmpUrl.GetLBText(i, strUrl);
std::string szUrl = cs2utf8(strUrl);
- m_rtcEngine->removePublishStreamUrl(szUrl.c_str());
+ m_rtcEngine->stopRtmpStream(szUrl.c_str());
}
m_cmbRtmpUrl.Clear();
m_cmbRtmpUrl.ResetContent();
@@ -393,7 +366,14 @@ void CAgoraRtmpStreamingDlg::OnBnClickedButtonAddstream()
std::string szURL = cs2utf8(strURL);
BOOL isTransCoding = m_chkTransCoding.GetCheck();
// add publish stream in the engine.
- int ret = m_rtcEngine->addPublishStreamUrl(szURL.c_str(), isTransCoding);
+ int ret;
+
+ if (isTransCoding) {
+ ret = m_rtcEngine->startRtmpStreamWithTranscoding(szURL.c_str(), m_liveTransCoding);
+ }
+ else {
+ ret = m_rtcEngine->startRtmpStreamWithoutTranscoding(szURL.c_str());
+ }
if (ret != 0) {
CString strInfo;
@@ -417,7 +397,7 @@ void CAgoraRtmpStreamingDlg::OnBnClickedButtonRemoveStream()
m_cmbRtmpUrl.GetWindowText(strUrl);
std::string szUrl = cs2utf8(strUrl);
//remove publish stream in the engine.
- m_rtcEngine->removePublishStreamUrl(szUrl.c_str());
+ m_rtcEngine->stopRtmpStream(szUrl.c_str());
}
//remove all streams in the engine.
@@ -434,7 +414,7 @@ void CAgoraRtmpStreamingDlg::OnBnClickedButtonRemoveAllstream()
std::string szUrl = cs2utf8(strUrl);
//remove public stream in the engine.
- m_rtcEngine->removePublishStreamUrl(szUrl.c_str());
+ m_rtcEngine->stopRtmpStream(szUrl.c_str());
m_btnRemoveStream.EnableWindow(FALSE);
}
@@ -479,7 +459,7 @@ LRESULT CAgoraRtmpStreamingDlg::OnEIDUserJoined(WPARAM wParam, LPARAM lParam)
//add user info to TranscodingUsers.
m_liveTransCoding.transcodingUsers = p;
//set current live trans coding.
- m_rtcEngine->setLiveTranscoding(m_liveTransCoding);
+ m_rtcEngine->updateRtmpTranscoding(m_liveTransCoding);
return TRUE;
}
@@ -505,7 +485,7 @@ LRESULT CAgoraRtmpStreamingDlg::OnEIDUserOffline(WPARAM wParam, LPARAM lParam)
m_liveTransCoding.transcodingUsers[i].width = width;
}
//set current live trans coding.
- m_rtcEngine->setLiveTranscoding(m_liveTransCoding);
+ m_rtcEngine->updateRtmpTranscoding(m_liveTransCoding);
return TRUE;
}
@@ -533,7 +513,7 @@ LRESULT CAgoraRtmpStreamingDlg::OnEIDRtmpStateChanged(WPARAM wParam, LPARAM lPar
{
case RTMP_STREAM_PUBLISH_STATE_IDLE:
{
- strInfo.Format(_T("%s:%S"), agoraRtmpStateIdle, rtmpState->url);
+ strInfo.Format(_T("%s:%S��"), agoraRtmpStateIdle, rtmpState->url);
CString strUrl;
strUrl.Format(_T("%S"), rtmpState->url);
int sel = m_cmbRtmpUrl.GetCurSel();
@@ -584,6 +564,51 @@ LRESULT CAgoraRtmpStreamingDlg::OnEIDRtmpStateChanged(WPARAM wParam, LPARAM lPar
break;
case RTMP_STREAM_PUBLISH_STATE_FAILURE:
{
+ strInfo = agoraRtmpStateRunningSuccess;
+ CString strUrl;
+ strUrl.Format(_T("%S"), rtmpState->url);
+ std::string szUrl = cs2utf8(strUrl);
+ m_rtcEngine->stopRtmpStream(szUrl.c_str());
+
+ int error = lParam;
+ if (error == RTMP_STREAM_PUBLISH_ERROR_CONNECTION_TIMEOUT
+ || error == RTMP_STREAM_PUBLISH_ERROR_INTERNAL_SERVER_ERROR
+ || error == RTMP_STREAM_PUBLISH_ERROR_STREAM_NOT_FOUND
+ || error == RTMP_STREAM_PUBLISH_ERROR_RTMP_SERVER_ERROR
+ || error == RTMP_STREAM_PUBLISH_ERROR_NET_DOWN) {
+ if (m_mapRepublishFlag.find(szUrl.c_str()) != m_mapRepublishFlag.end()
+ && m_mapRemoveFlag.find(szUrl.c_str()) != m_mapRemoveFlag.end()) {
+ if (m_mapRepublishFlag[szUrl.c_str()]
+ && !m_mapRemoveFlag[szUrl.c_str()]) {
+ //republish, removePublish when error
+ m_rtcEngine->startRtmpStreamWithoutTranscoding(szUrl.c_str());
+ }
+ }
+ }
+ else {
+ // stop retrying
+ m_mapRemoveFlag[szUrl.c_str()] = false;
+ m_mapRepublishFlag[szUrl.c_str()] = true;
+ CString strUrl;
+ strUrl.Format(_T("%S"), szUrl.c_str());
+ for (int i = 0; i < m_cmbRtmpUrl.GetCount(); ++i) {
+ CString strText;
+ m_cmbRtmpUrl.GetLBText(i, strText);
+ if (strText.Compare(strUrl) == 0) {
+ m_cmbRtmpUrl.DeleteString(i);
+ break;
+ }
+ }
+
+ if (m_urlSet.find(strUrl) != m_urlSet.end()) {
+ m_urlSet.erase(strUrl);
+ }
+
+ if (m_cmbRtmpUrl.GetCurSel() < 0 && m_cmbRtmpUrl.GetCount() > 0)
+ m_cmbRtmpUrl.SetCurSel(0);
+ }
+
+
switch (rtmpState->state)
{
case RTMP_STREAM_PUBLISH_ERROR_INVALID_ARGUMENT:
@@ -700,70 +725,6 @@ LRESULT CAgoraRtmpStreamingDlg::OnEIDRtmpEvent(WPARAM wParam, LPARAM lParam)
return 0;
}
-LRESULT CAgoraRtmpStreamingDlg::OnEIDStreamUnpublished(WPARAM wParam, LPARAM lParam)
-{
- char* url = (char*)wParam;
-
- if (m_mapRepublishFlag.find(url) != m_mapRepublishFlag.end()
- && m_mapRemoveFlag.find(url) != m_mapRemoveFlag.end()) {
- if (m_mapRepublishFlag[url]
- && !m_mapRemoveFlag[url]) {//republish, removePublish when error
- m_rtcEngine->addPublishStreamUrl(url, false);
- }
- }
-
- delete[] url;
- url = nullptr;
- return 0;
-}
-
-LRESULT CAgoraRtmpStreamingDlg::OnEIDStreamPublished(WPARAM wParam, LPARAM lParam)
-{
- char* url = (char*)wParam;
- int error = lParam;
-
- if (error == 1 || error == 10 || error == 154) {
- m_mapRemoveFlag[url] = false;
- m_rtcEngine->removePublishStreamUrl(url);
- m_mapRepublishFlag[url] = true;
- CString strUrl;
- strUrl.Format(_T("%S"), url);
- for (int i = 0; i < m_cmbRtmpUrl.GetCount(); ++i) {
- CString strText;
- m_cmbRtmpUrl.GetLBText(i, strText);
- if (strText.Compare(strUrl) == 0) {
- m_cmbRtmpUrl.DeleteString(i);
- break;
- }
- }
-
- if (m_urlSet.find(strUrl) != m_urlSet.end()) {
- m_urlSet.erase(strUrl);
- }
-
- if (m_cmbRtmpUrl.GetCurSel() < 0 && m_cmbRtmpUrl.GetCount() > 0)
- m_cmbRtmpUrl.SetCurSel(0);
- }
- else if (error == 155) {
- m_rtcEngine->addPublishStreamUrl(url, false);
-
- if (m_mapUrlToTimer.find(url) == m_mapUrlToTimer.end()) {
- LastTimer_Republish_id++;
- m_mapUrlToTimer[url] = LastTimer_Republish_id;
- m_mapTimerToUrl[LastTimer_Republish_id] = url;
- m_mapTimerToRepublishCount[LastTimer_Republish_id] = 1;
-
- SetTimer(LastTimer_Republish_id, 1000, NULL);
- }
-
- }
-
-
- delete[] url;
- url = nullptr;
- return 0;
-}
-
void CAgoraRtmpStreamingDlg::OnTimer(UINT_PTR nIDEvent)
{
@@ -778,6 +739,6 @@ void CAgoraRtmpStreamingDlg::OnTimer(UINT_PTR nIDEvent)
}
m_mapTimerToRepublishCount[nIDEvent]++;
- m_rtcEngine->addPublishStreamUrl(m_mapTimerToUrl[nIDEvent].c_str(), false);
+ m_rtcEngine->startRtmpStreamWithoutTranscoding(m_mapTimerToUrl[nIDEvent].c_str());
}
}
\ No newline at end of file
diff --git a/windows/APIExample/APIExample/Advanced/RTMPStream/AgoraRtmpStreaming.h b/windows/APIExample/APIExample/Advanced/RTMPStream/AgoraRtmpStreaming.h
index 823adb9c7..b7299fc7f 100644
--- a/windows/APIExample/APIExample/Advanced/RTMPStream/AgoraRtmpStreaming.h
+++ b/windows/APIExample/APIExample/Advanced/RTMPStream/AgoraRtmpStreaming.h
@@ -71,38 +71,7 @@ class CAgoraRtmpStreamingDlgRtcEngineEventHandler
@param state The RTMP streaming state. See: #RTMP_STREAM_PUBLISH_STATE.
@param errCode The detailed error information for streaming. See: #RTMP_STREAM_PUBLISH_ERROR.
*/
- virtual void onRtmpStreamingStateChanged(const char *url, RTMP_STREAM_PUBLISH_STATE state, RTMP_STREAM_PUBLISH_ERROR errCode)override;
-
- /** @deprecated This method is deprecated, use the \ref agora::rtc::IRtcEngineEventHandler::onRtmpStreamingStateChanged "onRtmpStreamingStateChanged" callback instead.
-
- Reports the result of calling the \ref agora::rtc::IRtcEngine::removePublishStreamUrl "removePublishStreamUrl" method. (CDN live only.)
-
- This callback indicates whether you have successfully removed an RTMP stream from the CDN.
-
- @param url The RTMP URL address.
- */
- virtual void onStreamUnpublished(const char *url) override;
-
- /** @deprecated This method is deprecated, use the \ref agora::rtc::IRtcEngineEventHandler::onRtmpStreamingStateChanged "onRtmpStreamingStateChanged" callback instead.
-
- Reports the result of calling the \ref IRtcEngine::addPublishStreamUrl "addPublishStreamUrl" method. (CDN live only.)
-
- @param url The RTMP URL address.
- @param error Error code: #ERROR_CODE_TYPE. Main errors include:
- - #ERR_OK (0): The publishing succeeds.
- - #ERR_FAILED (1): The publishing fails.
- - #ERR_INVALID_ARGUMENT (2): Invalid argument used. If, for example, you did not call \ref agora::rtc::IRtcEngine::setLiveTranscoding "setLiveTranscoding" to configure LiveTranscoding before calling \ref agora::rtc::IRtcEngine::addPublishStreamUrl "addPublishStreamUrl", the SDK reports #ERR_INVALID_ARGUMENT.
- - #ERR_TIMEDOUT (10): The publishing timed out.
- - #ERR_ALREADY_IN_USE (19): The chosen URL address is already in use for CDN live streaming.
- - #ERR_RESOURCE_LIMITED (22): The backend system does not have enough resources for the CDN live streaming.
- - #ERR_ENCRYPTED_STREAM_NOT_ALLOWED_PUBLISH (130): You cannot publish an encrypted stream.
- - #ERR_PUBLISH_STREAM_CDN_ERROR (151)
- - #ERR_PUBLISH_STREAM_NUM_REACH_LIMIT (152)
- - #ERR_PUBLISH_STREAM_NOT_AUTHORIZED (153)
- - #ERR_PUBLISH_STREAM_INTERNAL_SERVER_ERROR (154)
- - #ERR_PUBLISH_STREAM_FORMAT_NOT_SUPPORTED (156)
- */
- virtual void onStreamPublished(const char *url, int error) override;
+ virtual void onRtmpStreamingStateChanged(const char *url, RTMP_STREAM_PUBLISH_STATE state, RTMP_STREAM_PUBLISH_ERROR_TYPE errType)override;
virtual void onRtmpStreamingEvent(const char* url, RTMP_STREAMING_EVENT eventCode)override;
private:
@@ -169,8 +138,6 @@ class CAgoraRtmpStreamingDlg : public CDialogEx
afx_msg LRESULT OnEIDLeaveChannel(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnEIDRtmpStateChanged(WPARAM wParam, LPARAM lParam);
afx_msg void OnSelchangeListInfoBroadcasting();
- afx_msg LRESULT OnEIDStreamUnpublished(WPARAM wParam, LPARAM lParam);
- afx_msg LRESULT OnEIDStreamPublished(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnEIDRtmpEvent(WPARAM wParam, LPARAM lParam);
afx_msg void OnTimer(UINT_PTR nIDEvent);