WebRTC:播放MediaStream AudioTrack

时间:2019-05-17 06:47:04

标签: android webrtc

收到videoCall的远程流时的代码:

/**
 * Received remote peer's media stream. we will get the first video track and render it
 */
private void gotRemoteStream(MediaStream stream) {
    LogUtil.prependCallLocation("REMOTE STREAM GOTEN");
    //we have remote video stream. add to the renderer.

    final VideoTrack videoTrack = stream.videoTracks.get(0);
    runOnUiThread(() -> {
        try {
            videoTrack.addSink(remoteVideoView);
        } catch (Exception e) {
            e.printStackTrace();
            LogUtil.prependCallLocation("ERROR HAPPEND HERE");
        }
    });

}

现在,我正在将JUST音轨发送给同伴,因此我想播放该音轨。 (我只想在没有视频流的情况下实现语音通话)

新代码

private void gotRemoteStream(MediaStream stream) {
    LogUtil.prependCallLocation("REMOTE STREAM GOTEN");

    //Im getting Audio Track of the stream and i should play it somehow
    final AudioTrack audioTrack = stream.audioTracks.get(0);
    runOnUiThread(() -> {
        try {

        } catch (Exception e) {
            e.printStackTrace();
            LogUtil.prependCallLocation("ERROR HAPPEND HERE");
        }
    });
}

如何在本地播放audioTrack

1 个答案:

答案 0 :(得分:0)

已解决。

如果有人遇到相同的问题。 WebRTC自己处理音频流,您不必尝试播放它。

因此,如果您只想创建语音通话, 删除所有与视频渲染相关的请求,并且该请求有效。

在创建要约时要牢记,您必须从MediaConstraints中删除“ OfferToReceiveVideo”。

(在我的情况下,该崩溃导致没有日志错误)。

完成AudioCall活动类:

public class AudioCallActivity extends AppCompatActivity implements View.OnClickListener, SignallingClient.SignalingInterface {
    PeerConnectionFactory peerConnectionFactory;
    MediaConstraints audioConstraints;
    MediaConstraints sdpConstraints;
    AudioSource audioSource;
    AudioTrack localAudioTrack;


    Button hangup;
    PeerConnection localPeer;
    List<IceServer> iceServers;

    boolean gotUserMedia;


    private boolean hangedUp;

    BlastVisualizer mVisualizer;

    List<PeerConnection.IceServer> peerIceServers = new ArrayList<>();

    AudioManager audioManager;
    private String roomName;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_audio_call);

        initViews();

        getIceServers();

        roomName = Objects.requireNonNull(getIntent().getExtras()).getString("roomName",null);
        Toast.makeText(this, "roomName = " + roomName, Toast.LENGTH_SHORT).show();

    }


    private void initViews() {
        hangup = findViewById(R.id.end_call);
        hangup.setOnClickListener(this);
    }


    private void getIceServers() {
        hangedUp = false;
        //get Ice servers using xirsys
        byte[] data;
        data = ("yourICEServerAuthKey").getBytes(StandardCharsets.UTF_8);
        String authToken = "Basic " + Base64.encodeToString(data, Base64.NO_WRAP);
        Utils.getInstance().getRetrofitInstance().getIceCandidates(authToken).enqueue(new Callback<TurnServerPojo>() {
            @Override
            public void onResponse(@NonNull Call<TurnServerPojo> call, @NonNull Response<TurnServerPojo> response) {
                TurnServerPojo body = response.body();
                if (body != null) {
                    iceServers = body.iceServerList.iceServers;
                }
                // Toast.makeText(VideoCallActivity.this, "Ice Servers Received", Toast.LENGTH_SHORT).show();
                for (IceServer iceServer : iceServers) {
                    if (iceServer.credential == null) {
                        PeerConnection.IceServer peerIceServer =
                                PeerConnection
                                        .IceServer
                                        .builder(iceServer.url)
                                        .createIceServer();
                        peerIceServers
                                .add(peerIceServer);
                    } else {
                        PeerConnection.IceServer peerIceServer =
                                PeerConnection.IceServer.builder(iceServer.url)
                                        .setUsername(iceServer.username)
                                        .setPassword(iceServer.credential)
                                        .createIceServer();
                        peerIceServers.add(peerIceServer);
                    }
                }
                SignallingClient.getInstance().init(AudioCallActivity.this,roomName);

                start();
            }

            @Override
            public void onFailure(@NonNull Call<TurnServerPojo> call, @NonNull Throwable t) {
                t.printStackTrace();
                LogUtil.prependCallLocation("On Failure" + t);

            }
        });
    }


    public void start() {
        //Initialize PeerConnectionFactory globals.
        PeerConnectionFactory.InitializationOptions initializationOptions =
                PeerConnectionFactory.InitializationOptions.builder(this)
                        .createInitializationOptions();
        PeerConnectionFactory.initialize(initializationOptions);

        //Create a new PeerConnectionFactory instance - using Hardware encoder and decoder.
        PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
        peerConnectionFactory = PeerConnectionFactory.builder()
                .setOptions(options)
                .createPeerConnectionFactory();


        //Create MediaConstraints - Will be useful for specifying video and audio constraints.
        audioConstraints = new MediaConstraints();

        //create an AudioSource instance
        audioSource = peerConnectionFactory.createAudioSource(audioConstraints);
        localAudioTrack = peerConnectionFactory.createAudioTrack("101", audioSource);


        //showToast("GotUserMedia Sets True");
        gotUserMedia = true;
        if (SignallingClient.getInstance().isInitiator) {
            onTryToStart();
        }
    }


    /**
     * This method will be called directly by the app when it is the initiator and has got the local media
     * or when the remote peer sends a message through socket that it is ready to transmit AV data
     */
    @Override
    public void onTryToStart() {
        runOnUiThread(() -> {
            if (!SignallingClient.getInstance().isStarted && SignallingClient.getInstance().isChannelReady) {
                createPeerConnection();
                SignallingClient.getInstance().isStarted = true;
                if (SignallingClient.getInstance().isInitiator) {
                    doCall();
                }

            }
        });
    }


    /**
     * Creating the local peerconnection instance
     */
    private void createPeerConnection() {
        PeerConnection.RTCConfiguration rtcConfig =
                new PeerConnection.RTCConfiguration(peerIceServers);
        // TCP candidates are only useful when connecting to a server that supports
        // ICE-TCP.
        rtcConfig.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.DISABLED;
        rtcConfig.bundlePolicy = PeerConnection.BundlePolicy.MAXBUNDLE;
        rtcConfig.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.REQUIRE;
        rtcConfig.continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY;
        // Use ECDSA encryption.
        rtcConfig.keyType = PeerConnection.KeyType.ECDSA;
        localPeer = peerConnectionFactory.createPeerConnection(rtcConfig, new CustomPeerConnectionObserver("localPeerCreation") {
            @Override
            public void onIceCandidate(IceCandidate iceCandidate) {
                super.onIceCandidate(iceCandidate);
                onIceCandidateReceived(iceCandidate);
            }
            @Override
            public void onAddStream(MediaStream mediaStream) {

                //showToast("Received Remote stream");
                super.onAddStream(mediaStream);
                audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
                audioManager.setSpeakerphoneOn(false);
            }
        });

        addStreamToLocalPeer();
    }

    /**
     * Adding the stream to the localpeer
     */
    private void addStreamToLocalPeer() {
        MediaStream stream = peerConnectionFactory.createLocalMediaStream("101");
        stream.addTrack(localAudioTrack);
        localPeer.addStream(stream);
    }

    /**
     * This method is called when the app is initiator - We generate the offer and send it over through socket
     * to remote peer
     */
    private void doCall() {
        sdpConstraints = new MediaConstraints();
        sdpConstraints.mandatory.add(
                new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"));
        localPeer.createOffer(new CustomSdpObserver("localCreateOffer") {
            @Override
            public void onCreateSuccess(SessionDescription sessionDescription) {
                super.onCreateSuccess(sessionDescription);
                localPeer.setLocalDescription(new CustomSdpObserver("localSetLocalDesc"), sessionDescription);
                LogUtil.prependCallLocation("SignallingClient emit ");
                SignallingClient.getInstance().emitMessage(sessionDescription);
            }
        }, sdpConstraints);
    }



    /**
     * Received local ice candidate. Send it to remote peer through signalling for negotiation
     */
    public void onIceCandidateReceived(IceCandidate iceCandidate) {
        //we have received ice candidate. We can set it to the other peer.
        SignallingClient.getInstance().emitIceCandidate(iceCandidate);
    }

    /**
     * SignallingCallback - called when the room is created - i.e. you are the initiator
     */
    @Override
    public void onCreatedRoom() {
        if (gotUserMedia) {
            SignallingClient.getInstance().emitMessage("got user media");
        }
    }

    /**
     * SignallingCallback - called when you join the room - you are a participant
     */
    @Override
    public void onJoinedRoom() {
        if (gotUserMedia) {
            SignallingClient.getInstance().emitMessage("got user media");
        }
    }

    @Override
    public void onNewPeerJoined() {
        showToast("Remote Peer Joined");
    }

    @Override
    public void onHangupReqReceived(String msg) {
        runOnUiThread(this::hangup);
    }

    /**
     * SignallingCallback - Called when remote peer sends offer
     */
    @Override
    public void onOfferReceived(final JSONObject data) {
        //showToast("Received Offer");
        runOnUiThread(() -> {
            if (!SignallingClient.getInstance().isInitiator && !SignallingClient.getInstance().isStarted) {
                onTryToStart();
            }

            try {
                localPeer.setRemoteDescription(new CustomSdpObserver("localSetRemote"), new SessionDescription(SessionDescription.Type.OFFER, data.getString("sdp")));
                doAnswer();
            } catch (JSONException e) {
                e.printStackTrace();
            }
        });
    }

    private void doAnswer() {
        localPeer.createAnswer(new CustomSdpObserver("localCreateAns") {
            @Override
            public void onCreateSuccess(SessionDescription sessionDescription) {
                super.onCreateSuccess(sessionDescription);
                localPeer.setLocalDescription(new CustomSdpObserver("localSetLocal"), sessionDescription);
                SignallingClient.getInstance().emitMessage(sessionDescription);
            }
        }, new MediaConstraints());
    }

    /**
     * SignallingCallback - Called when remote peer sends answer to your offer
     */

    @Override
    public void onAnswerReceived(JSONObject data) {
        // showToast("Received Answer");
        try {
            localPeer.setRemoteDescription(new CustomSdpObserver("localSetRemote"), new SessionDescription(SessionDescription.Type.fromCanonicalForm(data.getString("type").toLowerCase()), data.getString("sdp")));
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    /**
     * Remote IceCandidate received
     */
    @Override
    public void onIceCandidateReceived(JSONObject data) {
        try {
            localPeer.addIceCandidate(new IceCandidate(data.getString("id"), data.getInt("label"), data.getString("candidate")));
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    /**
     * Closing up - normal hangup and app destroye
     */

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.end_call) {
            sendHangUpReq();
        }
    }
    private void sendHangUpReq() {
        SignallingClient.getInstance().sendHangupReq();
    }

    private void hangup() {
        if(hangedUp)return;
        hangedUp = true;
        try {
            if(localPeer != null)
                localPeer.close();
            localPeer= null;

            SignallingClient.getInstance().close();

            finish();

        } catch (Exception e) {
            Log.e("Exception Happend","ExceptionHappend");
            e.printStackTrace();
        }

    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        hangup();
        finish();

    }

    public void showToast(final String msg) {
        runOnUiThread(() -> Toast.makeText(AudioCallActivity.this, msg, Toast.LENGTH_SHORT).show());
    }

}