com.saylor.harrison.opustestround2.audio.WebSocketUploader.java Source code

Java tutorial

Introduction

Here is the source code for com.saylor.harrison.opustestround2.audio.WebSocketUploader.java

Source

/**
 *  Copyright IBM Corporation 2015
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/

package com.saylor.harrison.opustestround2.audio;

import android.util.Log;
import com.saylor.harrison.opustestround2.ISpeechDelegate;
import com.saylor.harrison.opustestround2.conf.SpeechConfiguration;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.channels.NotYetConnectedException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_17;
import org.java_websocket.handshake.ServerHandshake;
import org.json.JSONException;
import org.json.JSONObject;

public class WebSocketUploader extends WebSocketClient implements IChunkUploader {
    // Use PROPRIETARY notice if class contains a main() method, otherwise use COPYRIGHT notice.
    public static final String COPYRIGHT_NOTICE = "(c) Copyright IBM Corp. 2015";
    private static final String TAG = WebSocketUploader.class.getName();

    private ISpeechEncoder encoder = null;
    private Thread initStreamToServerThread;

    private boolean uploadPrepared = false;

    /** STT delegate */
    private ISpeechDelegate delegate = null;
    /** Recorder delegate */
    private SpeechConfiguration sConfig = null;

    /**
     * Create an uploader which supports streaming.
     *
     * @param serverURL LMC server, delivery to back end server
     * @throws URISyntaxException
     */
    public WebSocketUploader(String serverURL, Map<String, String> header, SpeechConfiguration config)
            throws URISyntaxException {
        super(new URI(serverURL), new Draft_17(), header, config.connectionTimeout);
        Log.d(TAG, "New WebSocketUploader: " + serverURL);
        Log.d(TAG, serverURL);
        this.sConfig = config;

        if (sConfig.audioFormat.equals(SpeechConfiguration.AUDIO_FORMAT_DEFAULT)) {
            this.encoder = new RawEnc();
        } else if (sConfig.audioFormat.equals(SpeechConfiguration.AUDIO_FORMAT_OGGOPUS)) {
            this.encoder = new OggOpusEnc();
        }

        if (serverURL.toLowerCase().startsWith("wss") || serverURL.toLowerCase().startsWith("https"))
            this.sConfig.isSSL = true;
        else
            this.sConfig.isSSL = false;
    }

    /**
     * Trust server
     *
     * @throws KeyManagementException
     * @throws NoSuchAlgorithmException
     */
    private void trustServer() throws KeyManagementException, NoSuchAlgorithmException, IOException {
        // Create a trust manager that does not validate certificate chains
        TrustManager[] certs = new TrustManager[] { new X509TrustManager() {
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return new java.security.cert.X509Certificate[] {};
            }

            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }
        } };
        SSLContext sslContext = null;
        sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, certs, new java.security.SecureRandom());
        SSLSocketFactory factory = sslContext.getSocketFactory();
        this.setSocket(factory.createSocket());
    }

    /**
     * 1. Initialize WebSocket connection </br>
     * 2. Init an encoder and writer
     *
     * @throws Exception
     */
    private void initStreamAudioToServer() throws Exception {
        Log.d(TAG, "Connecting...");
        //lifted up for initializing writer, using isRunning to control the flow
        this.encoder.initEncoderWithUploader(this);

        if (this.sConfig.isSSL)
            this.trustServer();

        boolean rc;
        rc = this.connectBlocking();

        if (rc) {
            Log.d(TAG, "Connected");
            this.sendSpeechHeader();
        } else {
            Log.e(TAG, "Connection failed!");
            this.uploadPrepared = false;
            throw new Exception("Connection failed!");
        }

    }

    @Override
    public int onHasData(byte[] buffer) {
        int uploadedAudioSize = 0;
        // NOW, WE HAVE STATUS OF UPLOAD PREPARING, UPLOAD PREPARING OK
        if (this.isUploadPrepared()) {
            try {
                uploadedAudioSize = encoder.encodeAndWrite(buffer);
                Log.d(TAG, "onHasData: " + uploadedAudioSize + " " + buffer.length);
                // TODO: Capturing data
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            try {
                Log.w(TAG, "waiting for establishing the connection");
                initStreamToServerThread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return uploadedAudioSize;
    }

    @Override
    public boolean isUploadPrepared() {
        return this.uploadPrepared;
    }

    public void stopUploaderPrepareThread() {
        if (initStreamToServerThread != null) {
            initStreamToServerThread.interrupt();
        }
    }

    /**
     * Prepare connection
     */
    @Override
    public void prepare() {
        this.uploadPrepared = false;
        initStreamToServerThread = new Thread() {
            public void run() {
                try {
                    try {
                        initStreamAudioToServer();
                        Log.d(TAG, "WebSocket Connection established");
                    } catch (IOException e1) {
                        Log.e(TAG, "IOException: " + e1.getMessage());
                        throw e1;
                    } catch (InterruptedException e1) {
                        Log.e(TAG, "InterruptedException:" + e1.getMessage());
                        throw e1;
                    } catch (Exception e1) {
                        Log.e(TAG, "Exception: " + e1.getMessage());
                        throw e1;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    Log.e(TAG, "Connection failed: " + (e == null ? "null exception" : e.getMessage()));
                    uploadPrepared = false;
                    close();
                }
            };
        };
        initStreamToServerThread.setName("initStreamToServerThread");
        initStreamToServerThread.start();
    }

    /**
     * Write string into socket
     *
     * @param message
     */
    public void upload(String message) {
        try {
            this.send(message);
        } catch (NotYetConnectedException ex) {
            Log.e(TAG, ex.getLocalizedMessage());
        }
    }

    /**
     * Write data into socket
     *
     * @param data
     */
    public void upload(byte[] data) {
        try {
            this.send(data);
        } catch (NotYetConnectedException ex) {
            Log.e(TAG, ex.getLocalizedMessage());
        }
    }

    /**
     * Stop by sending out zero byte of data
     */
    public void stop() {
        byte[] stopData = new byte[0];
        this.upload(stopData);
    }

    @Override
    public void close() {
        Log.d(TAG, "closing the websocket");
        super.close();
    }

    @Override
    public void onClose(int code, String reason, boolean remote) {
        Log.d(TAG, "WebSocket closed");
        this.uploadPrepared = false;

        Log.d(TAG, "### Code: " + code + " reason: " + reason + " remote: " + remote);
        if (delegate != null) {
            delegate.onClose(code, reason, remote);
        }
    }

    @Override
    public void onError(Exception ex) {
        Log.e(TAG, "WebSocket error");
        String errorMessage = "";
        if (ex != null)
            errorMessage = ex.getMessage();
        // Send the error message to the delegate
        this.uploadPrepared = false;
        //this.sendMessage(ISpeechDelegate.ERROR);
        if (delegate != null) {
            delegate.onError(errorMessage);
        }
    }

    @Override
    public void onMessage(String message) {

        Log.d(TAG + "onMessage", message);
        if (delegate != null) {
            delegate.onMessage(message);
        }
    }

    @Override
    public void onOpen(ServerHandshake arg0) {
        Log.d(TAG, "WS connection opened successfully");
        this.uploadPrepared = true;
        if (delegate != null) {
            delegate.onOpen();
        }
    }

    private void sendSpeechHeader() {

        JSONObject obj = new JSONObject();
        try {
            obj.put("action", "start");
            obj.put("content-type", this.sConfig.audioFormat);
            obj.put("interim_results", this.sConfig.returnInterimResults);
            obj.put("continuous", this.sConfig.continuous);
            obj.put("inactivity_timeout", this.sConfig.inactivityTimeout);

            if (this.sConfig.maxAlternatives > 1) {
                obj.put("max_alternatives", this.sConfig.maxAlternatives);
            }

            if (!(Float.isNaN(this.sConfig.wordAlternativesThreshold))) {
                obj.put("word_alternatives_threshold", this.sConfig.wordAlternativesThreshold);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        String startHeader = obj.toString();
        this.upload(startHeader);
        this.encoder.onStart();
        Log.d(TAG, "Sending init message: " + startHeader);
    }

    /**
     * Set delegate
     *
     * @param delegate
     */
    public void setDelegate(ISpeechDelegate delegate) {
        this.delegate = delegate;
    }
}