<commit_before>#include <AndroidClient.h>

#include <android/log.h>
#include <vector>
#include <memory>

using namespace std;

static jbyteArray convertToByteArray(JNIEnv * env, const std::string & s) {
  const jbyte * pNativeMessage = reinterpret_cast<const jbyte*>(s.c_str());
  jbyteArray bytes = env->NewByteArray(s.size());
  env->SetByteArrayRegion(bytes, 0, s.size(), pNativeMessage);
  return bytes;
}

static void logException(JNIEnv * env, const char * error) {
  jthrowable e = env->ExceptionOccurred();
  env->ExceptionClear();
  jclass clazz = env->GetObjectClass(e);
  jmethodID getMessage = env->GetMethodID(clazz, "getMessage", "()Ljava/lang/String;");
  jstring message = (jstring)env->CallObjectMethod(e, getMessage);
  string m;
  if (message != NULL) {
    const char *mstr = env->GetStringUTFChars(message, NULL);
    m = mstr;
    env->ReleaseStringUTFChars(message, mstr);
  }
  env->DeleteLocalRef(message);
  env->DeleteLocalRef(clazz);
  env->DeleteLocalRef(e);

  __android_log_print(ANDROID_LOG_INFO, "AndroidClient", "%s: %s", error, m.c_str());
}

class AndroidClientCache {
 public:
  AndroidClientCache(JNIEnv * myEnv) {
    myEnv->GetJavaVM(&javaVM);
    cookieManagerClass = (jclass) myEnv->NewGlobalRef(myEnv->FindClass("android/webkit/CookieManager"));
    httpClass = (jclass) myEnv->NewGlobalRef(myEnv->FindClass("java/net/HttpURLConnection"));
    urlClass = (jclass) myEnv->NewGlobalRef(myEnv->FindClass("java/net/URL"));
    urlConnectionClass = (jclass) myEnv->NewGlobalRef(myEnv->FindClass("java/net/URLConnection"));
    inputStreamClass = (jclass) myEnv->NewGlobalRef(myEnv->FindClass("java/io/InputStream"));
    frameworkClass = (jclass) myEnv->NewGlobalRef(myEnv->FindClass("com/sometrik/framework/FrameWork"));
    outputStreamClass = (jclass) myEnv->NewGlobalRef(myEnv->FindClass("java/io/OutputStream"));
    setReadTimeoutMethod = myEnv->GetMethodID(urlConnectionClass, "setReadTimeout", "(I)V");
    setConnectTimeoutMethod = myEnv->GetMethodID(urlConnectionClass, "setConnectTimeout", "(I)V");
    getHeaderMethod = myEnv->GetMethodID(httpClass, "getHeaderField", "(Ljava/lang/String;)Ljava/lang/String;");
    getHeaderMethodInt = myEnv->GetMethodID(httpClass, "getHeaderField", "(I)Ljava/lang/String;");
    getHeaderKeyMethod = myEnv->GetMethodID(httpClass, "getHeaderFieldKey", "(I)Ljava/lang/String;");
    readMethod = myEnv->GetMethodID(inputStreamClass, "read", "([B)I");
    urlConstructor = myEnv->GetMethodID(urlClass, "<init>", "(Ljava/lang/String;)V");
    openConnectionMethod = myEnv->GetMethodID(urlClass, "openConnection", "()Ljava/net/URLConnection;");
    setUseCachesMethod = myEnv->GetMethodID(urlConnectionClass, "setUseCaches", "(Z)V");
    disconnectConnectionMethod = myEnv->GetMethodID(httpClass, "disconnect", "()V");
    getOutputStreamMethod = myEnv->GetMethodID(urlConnectionClass, "getOutputStream", "()Ljava/io/OutputStream;");
    outputStreamWriteMethod = myEnv->GetMethodID(outputStreamClass, "write", "([B)V");
    setChunkedStreamingModeMethod = myEnv->GetMethodID(httpClass, "setChunkedStreamingMode", "(I)V");
    setFixedLengthStreamingModeMethod = myEnv->GetMethodID(httpClass, "setFixedLengthStreamingMode", "(I)V");
    setRequestProperty = myEnv->GetMethodID(httpClass, "setRequestProperty", "(Ljava/lang/String;Ljava/lang/String;)V");
    setRequestMethod = myEnv->GetMethodID(httpClass, "setRequestMethod", "(Ljava/lang/String;)V");
    setFollowMethod = myEnv->GetMethodID(httpClass, "setInstanceFollowRedirects", "(Z)V");
    setDoInputMethod = myEnv->GetMethodID(httpClass, "setDoInput", "(Z)V");
    setDoOutputMethod = myEnv->GetMethodID(httpClass, "setDoOutput", "(Z)V");
    getResponseCodeMethod = myEnv->GetMethodID(httpClass, "getResponseCode", "()I");
    getResponseMessageMethod = myEnv->GetMethodID(httpClass, "getResponseMessage", "()Ljava/lang/String;");
    setRequestPropertyMethod = myEnv->GetMethodID(httpClass, "setRequestProperty", "(Ljava/lang/String;Ljava/lang/String;)V");
    clearCookiesMethod = myEnv->GetMethodID(cookieManagerClass, "removeAllCookie", "()V");
    getInputStreamMethod = myEnv->GetMethodID(httpClass, "getInputStream", "()Ljava/io/InputStream;");
    getErrorStreamMethod = myEnv->GetMethodID(httpClass, "getErrorStream", "()Ljava/io/InputStream;");
    // handleThrowableMethod = myEnv->GetStaticMethodID(frameworkClass, "handleNativeException", "(Ljava/lang/Throwable;)V");
  }

  ~AndroidClientCache() {
    JNIEnv * env = getEnv();
    env->DeleteGlobalRef(cookieManagerClass);
    env->DeleteGlobalRef(httpClass);
    env->DeleteGlobalRef(urlClass);
    env->DeleteGlobalRef(inputStreamClass);
    env->DeleteGlobalRef(frameworkClass);
    env->DeleteGlobalRef(outputStreamClass);
  }

  JNIEnv * getEnv() {
    JNIEnv * env = 0;
    javaVM->GetEnv((void**)&env, JNI_VERSION_1_6);
    return env;
  }

  jclass cookieManagerClass;
  jmethodID clearCookiesMethod;
  jclass httpClass;
  jclass urlClass;
  jclass urlConnectionClass;
  jclass bufferedReaderClass;
  jclass inputStreamReaderClass;
  jclass inputStreamClass;
  jclass frameworkClass;
  jclass outputStreamClass;
  jmethodID urlConstructor;
  jmethodID openConnectionMethod;
  jmethodID getOutputStreamMethod;
  jmethodID outputStreamWriteMethod;
  jmethodID setReadTimeoutMethod;
  jmethodID setConnectTimeoutMethod;
  jmethodID setChunkedStreamingModeMethod;
  jmethodID setFixedLengthStreamingModeMethod;
  jmethodID setRequestProperty;
  jmethodID setRequestMethod;
  jmethodID setDoInputMethod;
  jmethodID setDoOutputMethod;
  jmethodID getResponseCodeMethod;
  jmethodID getResponseMessageMethod;
  jmethodID setRequestPropertyMethod;
  jmethodID outputStreamConstructor;
  jmethodID factoryDecodeMethod;
  jmethodID getInputStreamMethod;
  jmethodID getErrorStreamMethod;
  jmethodID bufferedReaderConstructor;
  jmethodID inputStreamReaderConstructor;
  jmethodID readLineMethod;
  jmethodID readerCloseMethod;
  jmethodID readMethod;
  jmethodID inputStreamCloseMethod;
  jmethodID setFollowMethod;
  jmethodID getHeaderMethod;
  jmethodID getHeaderMethodInt;
  jmethodID getHeaderKeyMethod;
  // jmethodID handleThrowableMethod;
  jmethodID setUseCachesMethod;
  jmethodID disconnectConnectionMethod;

 private:
  JavaVM * javaVM;
  JNIEnv * myEnv;
};

class AndroidClient : public HTTPClient {
public:
  AndroidClient(const std::shared_ptr<AndroidClientCache> & _cache, const std::string & _user_agent, bool _enable_cookies, bool _enable_keepalive)
    : HTTPClient(_user_agent, _enable_cookies, _enable_keepalive), cache(_cache) {
  }

  void request(const HTTPRequest & req, const Authorization & auth, HTTPClientInterface & callback) {
    JNIEnv * env = cache->getEnv();

    __android_log_print(ANDROID_LOG_INFO, "AndroidClient", "Host = %s, ua = %s", req.getURI().c_str(), user_agent.c_str());

    jstring juri = env->NewStringUTF(req.getURI().c_str());
    jobject url = env->NewObject(cache->urlClass, cache->urlConstructor, juri);
    env->DeleteLocalRef(juri);

    if (env->ExceptionCheck()) {
      logException(env, "Malformed URL");
      return;
    }

    jobject connection = env->CallObjectMethod(url, cache->openConnectionMethod);
    env->CallVoidMethod(connection, cache->setUseCachesMethod, JNI_FALSE);
    env->CallVoidMethod(connection, cache->setReadTimeoutMethod, req.getReadTimeout() * 1000);
    env->CallVoidMethod(connection, cache->setConnectTimeoutMethod, req.getConnectTimeout() * 1000);
    env->DeleteLocalRef(url);

    //Authorization example
//    env->CallVoidMethod(connection, setRequestPropertyMethod, env->NewStringUTF("Authorization"), env->NewStringUTF("myUsername"));
    std::string auth_header = auth.createHeader();
    env->CallVoidMethod(connection, cache->setFollowMethod, req.getFollowLocation() ? JNI_TRUE : JNI_FALSE);

    // Setting headers for request
    std::map<std::string, std::string> combined_headers;
    for (auto & hd : default_headers) {
      combined_headers[hd.first] = hd.second;
    }
    combined_headers["User-Agent"] = user_agent;
    if (req.getType() == HTTPRequest::POST && !req.getContentType().empty()) {
      combined_headers["Content-Type"] = req.getContentType();
    }
    for (auto & hd : req.getHeaders()) {
      combined_headers[hd.first] = hd.second;
    }
    if (!auth_header.empty()) {
      // __android_log_print(ANDROID_LOG_INFO, "AndroidClient", "Auth: %s = %s", auth.getHeaderName(), auth_header.c_str());
      combined_headers[auth.getHeaderName()] = auth_header;
    }
    for (auto & hd : combined_headers) {
      jstring firstHeader = env->NewStringUTF(hd.first.c_str());
      jstring secondHeader = env->NewStringUTF(hd.second.c_str());
      env->CallVoidMethod(connection, cache->setRequestPropertyMethod, firstHeader, secondHeader);
      env->DeleteLocalRef(firstHeader);
      env->DeleteLocalRef(secondHeader);
    }

    env->CallVoidMethod(connection, cache->setDoOutputMethod, req.getType() == HTTPRequest::POST);
    jstring jTypeString = env->NewStringUTF(req.getTypeString());
    env->CallVoidMethod(connection, cache->setRequestMethod, jTypeString);
    env->DeleteLocalRef(jTypeString);

    bool connection_failed = false;

    if (req.getType() == HTTPRequest::POST) {
      env->CallVoidMethod(connection, cache->setFixedLengthStreamingModeMethod, req.getContent().size());
      jobject outputStream = env->CallObjectMethod(connection, cache->getOutputStreamMethod);
      if (env->ExceptionCheck()) {
        logException(env, "Failed to open output stream");
	connection_failed = true;
      }

      for (int i = 0; i < req.getContent().size() && !connection_failed; i += 4096) {
        auto a = convertToByteArray(env, req.getContent().substr(i, 4096));
        env->CallVoidMethod(outputStream, cache->outputStreamWriteMethod, a);

	if (env->ExceptionCheck()) {
	  logException(env, "Failed to write post data");
	  connection_failed = true;
        }

	env->DeleteLocalRef(a);
      }
    }

    int responseCode = 0;
    if (!connection_failed) {
      responseCode = env->CallIntMethod(connection, cache->getResponseCodeMethod);
      if (env->ExceptionCheck()) {
        logException(env, "Failed to get response code");
	connection_failed = true;
      }
    }

    if (connection_failed) {
      callback.handleResultCode(0);
    } else {
      // __android_log_print(ANDROID_LOG_INFO, "AndroidClient", "http request responsecode = %i", responseCode);
      callback.handleResultCode(responseCode);

      jobject input = env->CallObjectMethod(connection, cache->getInputStreamMethod);
      if (env->ExceptionCheck()) {
        logException(env, "Failed to open input stream");

        input = env->CallObjectMethod(connection, cache->getErrorStreamMethod);

        if (env->ExceptionCheck()) {
          logException(env, "Failed to open error stream");
          input = 0;
        }
      }

      if (input != 0) {
	// Gather headers and values
	for (int i = 0;; i++) {
	  jstring jheaderKey = (jstring) env->CallObjectMethod(connection, cache->getHeaderKeyMethod, i);
	  if (jheaderKey == NULL) {
	    break;
	  } else {
	    jstring jheader = (jstring) env->CallObjectMethod(connection, cache->getHeaderMethodInt, i);
	    if (jheader != NULL) {
	      const char * headerKey = env->GetStringUTFChars(jheaderKey, 0);
	      const char * header = env->GetStringUTFChars(jheader, 0);
	      callback.handleHeader(headerKey, header);

	      if (strcasecmp(headerKey, "Location") == 0) {
	        callback.handleRedirectUrl(header);
	      }

	      env->ReleaseStringUTFChars(jheaderKey, headerKey);
              env->ReleaseStringUTFChars(jheader, header);
              env->DeleteLocalRef(jheader);
            }
	    env->DeleteLocalRef(jheaderKey);
	  }
	}

	// Gather content
	jbyteArray array = env->NewByteArray(4096);
	int g = 0;
	while ((g = env->CallIntMethod(input, cache->readMethod, array)) != -1) {
	  if (env->ExceptionCheck()) {
	    logException(env, "Failed to read stream");
	    break;
	  }
	  jbyte* content_array = env->GetByteArrayElements(array, NULL);
	  bool r = callback.handleChunk(g, (char*) content_array);
	  env->ReleaseByteArrayElements(array, content_array, JNI_ABORT);
	  if (!r || !callback.onIdle()) {
	    break;
	  }
	}
	env->DeleteLocalRef(array);
	env->DeleteLocalRef(input);
      }

      env->CallVoidMethod(connection, cache->disconnectConnectionMethod);
    }
    callback.handleDisconnect();

    env->DeleteLocalRef(connection);
  }

  void clearCookies() {
//    JNIEnv * env = cache->getJNIEnv();
//    env->CallVoidMethod(cache->cookieManagerClass, cache->clearCookiesMethod);
  }

private:
  std::shared_ptr<AndroidClientCache> cache;
};

std::shared_ptr<AndroidClientCache> AndroidClientFactory::cache;

void
AndroidClientFactory::initialize(JNIEnv * env) {
  cache = make_shared<AndroidClientCache>(env);
}

std::unique_ptr<HTTPClient>
AndroidClientFactory::createClient2(const std::string & _user_agent, bool _enable_cookies, bool _enable_keepalive) {
  return std::unique_ptr<AndroidClient>(new AndroidClient(cache, _user_agent, _enable_cookies, _enable_keepalive));
}
<commit_msg>use toString() to get exception info, modify logging<commit_after>#include <AndroidClient.h>

#include <android/log.h>
#include <vector>
#include <memory>

using namespace std;

static jbyteArray convertToByteArray(JNIEnv * env, const std::string & s) {
  const jbyte * pNativeMessage = reinterpret_cast<const jbyte*>(s.c_str());
  jbyteArray bytes = env->NewByteArray(s.size());
  env->SetByteArrayRegion(bytes, 0, s.size(), pNativeMessage);
  return bytes;
}

static void logException(JNIEnv * env, const char * error) {
  jthrowable e = env->ExceptionOccurred();
  env->ExceptionClear();
  jclass clazz = env->GetObjectClass(e);
  jmethodID getMessage = env->GetMethodID(clazz, "toString", "()Ljava/lang/String;"); // or getMessage
  jstring message = (jstring)env->CallObjectMethod(e, getMessage);
  string m;
  if (message != NULL) {
    const char *mstr = env->GetStringUTFChars(message, NULL);
    m = mstr;
    env->ReleaseStringUTFChars(message, mstr);
  }
  env->DeleteLocalRef(message);
  env->DeleteLocalRef(clazz);
  env->DeleteLocalRef(e);

  __android_log_print(ANDROID_LOG_INFO, "AndroidClient", "%s: %s", error, m.c_str());
}

class AndroidClientCache {
 public:
  AndroidClientCache(JNIEnv * myEnv) {
    myEnv->GetJavaVM(&javaVM);
    cookieManagerClass = (jclass) myEnv->NewGlobalRef(myEnv->FindClass("android/webkit/CookieManager"));
    httpClass = (jclass) myEnv->NewGlobalRef(myEnv->FindClass("java/net/HttpURLConnection"));
    urlClass = (jclass) myEnv->NewGlobalRef(myEnv->FindClass("java/net/URL"));
    urlConnectionClass = (jclass) myEnv->NewGlobalRef(myEnv->FindClass("java/net/URLConnection"));
    inputStreamClass = (jclass) myEnv->NewGlobalRef(myEnv->FindClass("java/io/InputStream"));
    frameworkClass = (jclass) myEnv->NewGlobalRef(myEnv->FindClass("com/sometrik/framework/FrameWork"));
    outputStreamClass = (jclass) myEnv->NewGlobalRef(myEnv->FindClass("java/io/OutputStream"));
    setReadTimeoutMethod = myEnv->GetMethodID(urlConnectionClass, "setReadTimeout", "(I)V");
    setConnectTimeoutMethod = myEnv->GetMethodID(urlConnectionClass, "setConnectTimeout", "(I)V");
    getHeaderMethod = myEnv->GetMethodID(httpClass, "getHeaderField", "(Ljava/lang/String;)Ljava/lang/String;");
    getHeaderMethodInt = myEnv->GetMethodID(httpClass, "getHeaderField", "(I)Ljava/lang/String;");
    getHeaderKeyMethod = myEnv->GetMethodID(httpClass, "getHeaderFieldKey", "(I)Ljava/lang/String;");
    readMethod = myEnv->GetMethodID(inputStreamClass, "read", "([B)I");
    urlConstructor = myEnv->GetMethodID(urlClass, "<init>", "(Ljava/lang/String;)V");
    openConnectionMethod = myEnv->GetMethodID(urlClass, "openConnection", "()Ljava/net/URLConnection;");
    setUseCachesMethod = myEnv->GetMethodID(urlConnectionClass, "setUseCaches", "(Z)V");
    disconnectConnectionMethod = myEnv->GetMethodID(httpClass, "disconnect", "()V");
    getOutputStreamMethod = myEnv->GetMethodID(urlConnectionClass, "getOutputStream", "()Ljava/io/OutputStream;");
    outputStreamWriteMethod = myEnv->GetMethodID(outputStreamClass, "write", "([B)V");
    setChunkedStreamingModeMethod = myEnv->GetMethodID(httpClass, "setChunkedStreamingMode", "(I)V");
    setFixedLengthStreamingModeMethod = myEnv->GetMethodID(httpClass, "setFixedLengthStreamingMode", "(I)V");
    setRequestProperty = myEnv->GetMethodID(httpClass, "setRequestProperty", "(Ljava/lang/String;Ljava/lang/String;)V");
    setRequestMethod = myEnv->GetMethodID(httpClass, "setRequestMethod", "(Ljava/lang/String;)V");
    setFollowMethod = myEnv->GetMethodID(httpClass, "setInstanceFollowRedirects", "(Z)V");
    setDoInputMethod = myEnv->GetMethodID(httpClass, "setDoInput", "(Z)V");
    setDoOutputMethod = myEnv->GetMethodID(httpClass, "setDoOutput", "(Z)V");
    getResponseCodeMethod = myEnv->GetMethodID(httpClass, "getResponseCode", "()I");
    getResponseMessageMethod = myEnv->GetMethodID(httpClass, "getResponseMessage", "()Ljava/lang/String;");
    setRequestPropertyMethod = myEnv->GetMethodID(httpClass, "setRequestProperty", "(Ljava/lang/String;Ljava/lang/String;)V");
    clearCookiesMethod = myEnv->GetMethodID(cookieManagerClass, "removeAllCookie", "()V");
    getInputStreamMethod = myEnv->GetMethodID(httpClass, "getInputStream", "()Ljava/io/InputStream;");
    getErrorStreamMethod = myEnv->GetMethodID(httpClass, "getErrorStream", "()Ljava/io/InputStream;");
    // handleThrowableMethod = myEnv->GetStaticMethodID(frameworkClass, "handleNativeException", "(Ljava/lang/Throwable;)V");
  }

  ~AndroidClientCache() {
    JNIEnv * env = getEnv();
    env->DeleteGlobalRef(cookieManagerClass);
    env->DeleteGlobalRef(httpClass);
    env->DeleteGlobalRef(urlClass);
    env->DeleteGlobalRef(inputStreamClass);
    env->DeleteGlobalRef(frameworkClass);
    env->DeleteGlobalRef(outputStreamClass);
  }

  JNIEnv * getEnv() {
    JNIEnv * env = 0;
    javaVM->GetEnv((void**)&env, JNI_VERSION_1_6);
    return env;
  }

  jclass cookieManagerClass;
  jmethodID clearCookiesMethod;
  jclass httpClass;
  jclass urlClass;
  jclass urlConnectionClass;
  jclass bufferedReaderClass;
  jclass inputStreamReaderClass;
  jclass inputStreamClass;
  jclass frameworkClass;
  jclass outputStreamClass;
  jmethodID urlConstructor;
  jmethodID openConnectionMethod;
  jmethodID getOutputStreamMethod;
  jmethodID outputStreamWriteMethod;
  jmethodID setReadTimeoutMethod;
  jmethodID setConnectTimeoutMethod;
  jmethodID setChunkedStreamingModeMethod;
  jmethodID setFixedLengthStreamingModeMethod;
  jmethodID setRequestProperty;
  jmethodID setRequestMethod;
  jmethodID setDoInputMethod;
  jmethodID setDoOutputMethod;
  jmethodID getResponseCodeMethod;
  jmethodID getResponseMessageMethod;
  jmethodID setRequestPropertyMethod;
  jmethodID outputStreamConstructor;
  jmethodID factoryDecodeMethod;
  jmethodID getInputStreamMethod;
  jmethodID getErrorStreamMethod;
  jmethodID bufferedReaderConstructor;
  jmethodID inputStreamReaderConstructor;
  jmethodID readLineMethod;
  jmethodID readerCloseMethod;
  jmethodID readMethod;
  jmethodID inputStreamCloseMethod;
  jmethodID setFollowMethod;
  jmethodID getHeaderMethod;
  jmethodID getHeaderMethodInt;
  jmethodID getHeaderKeyMethod;
  // jmethodID handleThrowableMethod;
  jmethodID setUseCachesMethod;
  jmethodID disconnectConnectionMethod;

 private:
  JavaVM * javaVM;
  JNIEnv * myEnv;
};

class AndroidClient : public HTTPClient {
public:
  AndroidClient(const std::shared_ptr<AndroidClientCache> & _cache, const std::string & _user_agent, bool _enable_cookies, bool _enable_keepalive)
    : HTTPClient(_user_agent, _enable_cookies, _enable_keepalive), cache(_cache) {
  }

  void request(const HTTPRequest & req, const Authorization & auth, HTTPClientInterface & callback) {
    JNIEnv * env = cache->getEnv();

    // __android_log_print(ANDROID_LOG_INFO, "AndroidClient", "Host = %s, ua = %s", req.getURI().c_str(), user_agent.c_str());

    jstring juri = env->NewStringUTF(req.getURI().c_str());
    jobject url = env->NewObject(cache->urlClass, cache->urlConstructor, juri);
    env->DeleteLocalRef(juri);

    if (env->ExceptionCheck()) {
      logException(env, "Malformed URL");
      return;
    }

    jobject connection = env->CallObjectMethod(url, cache->openConnectionMethod);
    env->CallVoidMethod(connection, cache->setUseCachesMethod, JNI_FALSE);
    env->CallVoidMethod(connection, cache->setReadTimeoutMethod, req.getReadTimeout() * 1000);
    env->CallVoidMethod(connection, cache->setConnectTimeoutMethod, req.getConnectTimeout() * 1000);
    env->DeleteLocalRef(url);

    //Authorization example
//    env->CallVoidMethod(connection, setRequestPropertyMethod, env->NewStringUTF("Authorization"), env->NewStringUTF("myUsername"));
    std::string auth_header = auth.createHeader();
    env->CallVoidMethod(connection, cache->setFollowMethod, req.getFollowLocation() ? JNI_TRUE : JNI_FALSE);

    // Setting headers for request
    std::map<std::string, std::string> combined_headers;
    for (auto & hd : default_headers) {
      combined_headers[hd.first] = hd.second;
    }
    combined_headers["User-Agent"] = user_agent;
    if (req.getType() == HTTPRequest::POST && !req.getContentType().empty()) {
      combined_headers["Content-Type"] = req.getContentType();
    }
    for (auto & hd : req.getHeaders()) {
      combined_headers[hd.first] = hd.second;
    }
    if (!auth_header.empty()) {
      // __android_log_print(ANDROID_LOG_INFO, "AndroidClient", "Auth: %s = %s", auth.getHeaderName(), auth_header.c_str());
      combined_headers[auth.getHeaderName()] = auth_header;
    }
    for (auto & hd : combined_headers) {
      jstring firstHeader = env->NewStringUTF(hd.first.c_str());
      jstring secondHeader = env->NewStringUTF(hd.second.c_str());
      env->CallVoidMethod(connection, cache->setRequestPropertyMethod, firstHeader, secondHeader);
      env->DeleteLocalRef(firstHeader);
      env->DeleteLocalRef(secondHeader);
    }

    env->CallVoidMethod(connection, cache->setDoOutputMethod, req.getType() == HTTPRequest::POST);
    jstring jTypeString = env->NewStringUTF(req.getTypeString());
    env->CallVoidMethod(connection, cache->setRequestMethod, jTypeString);
    env->DeleteLocalRef(jTypeString);

    bool connection_failed = false;

    if (req.getType() == HTTPRequest::POST) {
      env->CallVoidMethod(connection, cache->setFixedLengthStreamingModeMethod, req.getContent().size());
      jobject outputStream = env->CallObjectMethod(connection, cache->getOutputStreamMethod);
      if (env->ExceptionCheck()) {
        logException(env, "Failed to open output stream");
	connection_failed = true;
      }

      for (int i = 0; i < req.getContent().size() && !connection_failed; i += 4096) {
        auto a = convertToByteArray(env, req.getContent().substr(i, 4096));
        env->CallVoidMethod(outputStream, cache->outputStreamWriteMethod, a);

	if (env->ExceptionCheck()) {
	  logException(env, "Failed to write post data");
	  connection_failed = true;
        }

	env->DeleteLocalRef(a);
      }
    }

    int responseCode = 0;
    if (!connection_failed) {
      responseCode = env->CallIntMethod(connection, cache->getResponseCodeMethod);
      if (env->ExceptionCheck()) {
        logException(env, "Failed to get response code");
	connection_failed = true;
      }
    }

    if (connection_failed) {
      callback.handleResultCode(0);
    } else {
      __android_log_print(ANDROID_LOG_INFO, "AndroidClient", "http request responsecode = %i", responseCode);
      callback.handleResultCode(responseCode);

      jobject input = env->CallObjectMethod(connection, cache->getInputStreamMethod);
      if (env->ExceptionCheck()) {
        logException(env, "Failed to open input stream");

        input = env->CallObjectMethod(connection, cache->getErrorStreamMethod);

        if (env->ExceptionCheck()) {
          logException(env, "Failed to open error stream");
          input = 0;
        }
      }

      if (input != 0) {
	// Gather headers and values
	for (int i = 0;; i++) {
	  jstring jheaderKey = (jstring) env->CallObjectMethod(connection, cache->getHeaderKeyMethod, i);
	  if (jheaderKey == NULL) {
	    break;
	  } else {
	    jstring jheader = (jstring) env->CallObjectMethod(connection, cache->getHeaderMethodInt, i);
	    if (jheader != NULL) {
	      const char * headerKey = env->GetStringUTFChars(jheaderKey, 0);
	      const char * header = env->GetStringUTFChars(jheader, 0);
	      callback.handleHeader(headerKey, header);

	      if (strcasecmp(headerKey, "Location") == 0) {
	        callback.handleRedirectUrl(header);
	      }

	      env->ReleaseStringUTFChars(jheaderKey, headerKey);
              env->ReleaseStringUTFChars(jheader, header);
              env->DeleteLocalRef(jheader);
            }
	    env->DeleteLocalRef(jheaderKey);
	  }
	}

	// Gather content
	jbyteArray array = env->NewByteArray(4096);
	int g = 0;
	while ((g = env->CallIntMethod(input, cache->readMethod, array)) != -1) {
	  if (env->ExceptionCheck()) {
	    logException(env, "Failed to read stream");
	    break;
	  }
	  jbyte* content_array = env->GetByteArrayElements(array, NULL);
	  bool r = callback.handleChunk(g, (char*) content_array);
	  env->ReleaseByteArrayElements(array, content_array, JNI_ABORT);
	  if (!r || !callback.onIdle()) {
	    break;
	  }
	}
	env->DeleteLocalRef(array);
	env->DeleteLocalRef(input);
      }

      env->CallVoidMethod(connection, cache->disconnectConnectionMethod);
    }
    callback.handleDisconnect();

    env->DeleteLocalRef(connection);
  }

  void clearCookies() {
//    JNIEnv * env = cache->getJNIEnv();
//    env->CallVoidMethod(cache->cookieManagerClass, cache->clearCookiesMethod);
  }

private:
  std::shared_ptr<AndroidClientCache> cache;
};

std::shared_ptr<AndroidClientCache> AndroidClientFactory::cache;

void
AndroidClientFactory::initialize(JNIEnv * env) {
  cache = make_shared<AndroidClientCache>(env);
}

std::unique_ptr<HTTPClient>
AndroidClientFactory::createClient2(const std::string & _user_agent, bool _enable_cookies, bool _enable_keepalive) {
  return std::unique_ptr<AndroidClient>(new AndroidClient(cache, _user_agent, _enable_cookies, _enable_keepalive));
}
<|endoftext|>


<commit_before>#include "pch.h"
#include "AudioRenderer.h"

namespace SaneAudioRenderer
{
    namespace
    {
        DspChunk PreProcess(IMediaSample* pSample, const AM_SAMPLE2_PROPERTIES& sampleProps,
                            const WAVEFORMATEX& sampleFormat, REFERENCE_TIME previousSampleEndTime)
        {
            DspChunk chunk;

            // TODO: can gradually accumulate error - decide what to do with it

            auto timeToFrames = [&](REFERENCE_TIME time)
            {
                return (size_t)(time * sampleFormat.nSamplesPerSec / OneSecond);
            };

            if (sampleProps.tStop <= previousSampleEndTime)
            {
                // Drop the sample.
                assert(chunk.IsEmpty());
            }
            else if (sampleProps.tStart < previousSampleEndTime)
            {
                // Crop the sample.
                size_t cropFrames = timeToFrames(previousSampleEndTime - sampleProps.tStart);
                if (cropFrames > 0)
                {
                    size_t cropBytes = cropFrames * sampleFormat.nChannels * sampleFormat.wBitsPerSample / 8;

                    AM_SAMPLE2_PROPERTIES croppedSampleProps = sampleProps;
                    assert((int32_t)cropBytes < croppedSampleProps.lActual);
                    croppedSampleProps.pbBuffer += cropBytes;
                    croppedSampleProps.lActual -= (int32_t)cropBytes;

                    chunk = DspChunk(pSample, croppedSampleProps, sampleFormat);
                }
                else
                {
                    chunk = DspChunk(pSample, sampleProps, sampleFormat);
                }
            }
            else if (sampleProps.tStart > previousSampleEndTime)
            {
                // Zero-pad the sample.
                size_t extendFrames = timeToFrames(sampleProps.tStart - previousSampleEndTime);
                if (extendFrames > 0)
                {
                    DspChunk tempChunk(pSample, sampleProps, sampleFormat);

                    chunk = DspChunk(tempChunk.GetFormat(), tempChunk.GetChannelCount(),
                                     tempChunk.GetFrameCount() + extendFrames, tempChunk.GetRate());

                    size_t extendBytes = extendFrames * chunk.GetFrameSize();

                    assert(chunk.GetSize() == tempChunk.GetSize() + extendBytes);
                    ZeroMemory(chunk.GetData(), extendBytes);
                    memcpy(chunk.GetData() + extendBytes, tempChunk.GetConstData(), tempChunk.GetSize());
                }
                else
                {
                    chunk = DspChunk(pSample, sampleProps, sampleFormat);
                }
            }
            else
            {
                // Leave the sample untouched.
                chunk = DspChunk(pSample, sampleProps, sampleFormat);
            }

            return chunk;
        }
    }

    AudioRenderer::AudioRenderer(ISettings* pSettings, IMyClock* pClock, CAMEvent& bufferFilled, HRESULT& result)
        : m_deviceManager(result)
        , m_graphClock(pClock)
        , m_flush(TRUE/*manual reset*/)
        , m_dspVolume(*this)
        , m_dspBalance(*this)
        , m_bufferFilled(bufferFilled)
        , m_settings(pSettings)
    {
        if (FAILED(result))
            return;

        try
        {
            if (!m_settings || !m_graphClock)
                throw E_UNEXPECTED;

            if (static_cast<HANDLE>(m_flush) == NULL ||
                static_cast<HANDLE>(m_bufferFilled) == NULL)
            {
                throw E_OUTOFMEMORY;
            }
        }
        catch (HRESULT ex)
        {
            result = ex;
        }
    }

    AudioRenderer::~AudioRenderer()
    {
        // Just in case.
        if (m_state != State_Stopped)
            Stop();

        m_device = {};
    }

    bool AudioRenderer::Enqueue(IMediaSample* pSample, const AM_SAMPLE2_PROPERTIES& sampleProps)
    {
        DspChunk chunk;

        {
            CAutoLock objectLock(this);
            assert(m_state != State_Stopped);

            if (!m_deviceInitialized &&
                m_deviceManager.CreateDevice(m_device, m_inputFormat, !!m_settings->UseExclusiveMode()))
            {
                m_deviceInitialized = true;

                assert(m_inputFormatInitialized);
                InitializeProcessors();

                m_startOffset = m_lastSampleEnd;

                if (m_state == State_Running)
                    StartDevice();
            }

            try
            {
                chunk = PreProcess(pSample, sampleProps, m_inputFormat.Format, m_lastSampleEnd);

                if (chunk.IsEmpty())
                    return true;

                if (m_deviceInitialized)
                {
                    m_dspMatrix.Process(chunk);
                    m_dspRate.Process(chunk);
                    m_dspTempo.Process(chunk);
                    m_dspCrossfeed.Process(chunk);
                    m_dspVolume.Process(chunk);
                    m_dspBalance.Process(chunk);
                    m_dspLimiter.Process(chunk);
                    m_dspDither.Process(chunk);

                    DspChunk::ToFormat(m_device.dspFormat, chunk);
                }

                m_lastSampleEnd = sampleProps.tStop;
            }
            catch (std::bad_alloc&)
            {
                chunk = DspChunk();
                assert(chunk.IsEmpty());
            }
        }

        return Push(chunk);
    }

    bool AudioRenderer::Finish(bool blockUntilEnd)
    {
        DspChunk chunk;

        {
            CAutoLock objectLock(this);
            assert(m_state != State_Stopped);

            if (!m_deviceInitialized)
                blockUntilEnd = false;

            try
            {
                if (m_deviceInitialized)
                {
                    m_dspMatrix.Finish(chunk);
                    m_dspRate.Finish(chunk);
                    m_dspTempo.Finish(chunk);
                    m_dspCrossfeed.Finish(chunk);
                    m_dspVolume.Finish(chunk);
                    m_dspBalance.Finish(chunk);
                    m_dspLimiter.Finish(chunk);
                    m_dspDither.Finish(chunk);

                    DspChunk::ToFormat(m_device.dspFormat, chunk);
                }
            }
            catch (std::bad_alloc&)
            {
                chunk = DspChunk();
                assert(chunk.IsEmpty());
            }
        }

        auto doBlock = [this]
        {
            TimePeriodHelper timePeriodHelper(1);

            m_graphClock->UnslaveClockFromAudio();

            for (;;)
            {
                int64_t actual, target;

                {
                    CAutoLock objectLock(this);

                    assert(m_deviceInitialized);

                    UINT64 deviceClockFrequency, deviceClockPosition;

                    try
                    {
                        ThrowIfFailed(m_device.audioClock->GetFrequency(&deviceClockFrequency));
                        ThrowIfFailed(m_device.audioClock->GetPosition(&deviceClockPosition, nullptr));
                    }
                    catch (HRESULT)
                    {
                        ClearDevice();
                        return true;
                    }

                    actual = llMulDiv(deviceClockPosition, OneSecond, deviceClockFrequency, 0);
                    target = llMulDiv(m_pushedFrames, OneSecond, m_device.format.Format.nSamplesPerSec, 0);

                    if (actual >= target)
                    {
                        assert(actual == target);
                        break;
                    }
                }

                if (m_flush.Wait(std::max(1, (int32_t)((target - actual) * 1000 / OneSecond))))
                    return false;
            }

            return true;
        };

        return Push(chunk) && (!blockUntilEnd || doBlock());
    }

    void AudioRenderer::BeginFlush()
    {
        m_flush.Set();
    }

    void AudioRenderer::EndFlush()
    {
        CAutoLock objectLock(this);
        assert(m_state != State_Running);

        if (m_deviceInitialized)
        {
            m_device.audioClient->Reset();
            m_bufferFilled.Reset();
        }

        m_flush.Reset();

        m_pushedFrames = 0;
    }

    void AudioRenderer::SetFormat(const WAVEFORMATEX& inputFormat)
    {
        CAutoLock objectLock(this);

        m_inputFormat = inputFormat.wFormatTag == WAVE_FORMAT_EXTENSIBLE ?
                            reinterpret_cast<const WAVEFORMATEXTENSIBLE&>(inputFormat) :
                            WAVEFORMATEXTENSIBLE{inputFormat};

        m_inputFormatInitialized = true;

        ClearDevice();
    }

    void AudioRenderer::NewSegment(double rate)
    {
        CAutoLock objectLock(this);

        m_startOffset = 0;
        m_lastSampleEnd = 0;
        m_rate = rate;

        assert(m_inputFormatInitialized);
        if (m_deviceInitialized)
            InitializeProcessors();
    }

    void AudioRenderer::Play(REFERENCE_TIME startTime)
    {
        CAutoLock objectLock(this);
        assert(m_state != State_Running);
        m_state = State_Running;

        m_startTime = startTime;
        StartDevice();
    }

    void AudioRenderer::Pause()
    {
        CAutoLock objectLock(this);
        m_state = State_Paused;

        if (m_deviceInitialized)
        {
            m_graphClock->UnslaveClockFromAudio();
            m_device.audioClient->Stop();
        }
    }

    void AudioRenderer::Stop()
    {
        CAutoLock objectLock(this);
        m_state = State_Stopped;

        ClearDevice();
    }

    std::unique_ptr<WAVEFORMATEXTENSIBLE> AudioRenderer::GetInputFormat()
    {
        CAutoLock objectLock(this);

        if (!m_inputFormatInitialized)
            return nullptr;

        return std::make_unique<WAVEFORMATEXTENSIBLE>(m_inputFormat);
    }

    std::unique_ptr<AudioDevice> AudioRenderer::GetDeviceFormat()
    {
        CAutoLock objectLock(this);

        if (!m_deviceInitialized)
            return nullptr;

        return std::make_unique<AudioDevice>(m_device);
    }

    void AudioRenderer::StartDevice()
    {
        CAutoLock objectLock(this);
        assert(m_state == State_Running);

        if (m_deviceInitialized)
        {
            m_graphClock->SlaveClockToAudio(m_device.audioClock, m_startTime + m_startOffset);
            m_startOffset = 0;
            m_device.audioClient->Start();
            //assert(m_bufferFilled.Check());
        }
    }

    void AudioRenderer::ClearDevice()
    {
        CAutoLock objectLock(this);

        if (m_deviceInitialized)
        {
            m_graphClock->UnslaveClockFromAudio();
            m_device.audioClient->Stop();
            m_bufferFilled.Reset();
        }

        m_deviceInitialized = false;
        m_device = {};
        m_deviceManager.ReleaseDevice();
    }

    void AudioRenderer::InitializeProcessors()
    {
        CAutoLock objectLock(this);
        assert(m_inputFormatInitialized);
        assert(m_deviceInitialized);

        const auto inRate = m_inputFormat.Format.nSamplesPerSec;
        const auto inChannels = m_inputFormat.Format.nChannels;
        const auto inMask = DspMatrix::GetChannelMask(m_inputFormat);
        const auto outRate = m_device.format.Format.nSamplesPerSec;
        const auto outChannels = m_device.format.Format.nChannels;
        const auto outMask = DspMatrix::GetChannelMask(m_device.format);

        m_dspMatrix.Initialize(inChannels, inMask, outChannels, outMask);
        m_dspRate.Initialize(inRate, outRate, outChannels);
        m_dspTempo.Initialize((float)m_rate, outRate, outChannels);
        m_dspCrossfeed.Initialize(!!m_settings->UseStereoCrossfeed(), outRate, outChannels, outMask);
        m_dspVolume.Initialize(m_device.exclusive);
        m_dspLimiter.Initialize(outRate, m_device.exclusive);
        m_dspDither.Initialize(m_device.dspFormat);
    }

    bool AudioRenderer::Push(DspChunk& chunk)
    {
        const uint32_t frameSize = chunk.GetFrameSize();
        const size_t chunkFrames = chunk.GetFrameCount();

        bool firstIteration = true;
        for (size_t doneFrames = 0; doneFrames < chunkFrames;)
        {
            // The device buffer is full or almost full at the beginning of the second and subsequent iterations.
            // Sleep until the buffer may have significant amount of free space. Unless interrupted.
            if (!firstIteration && m_flush.Wait(m_device.bufferDuration / 4))
                return false;

            firstIteration = false;

            CAutoLock objectLock(this);

            assert(m_state != State_Stopped);

            if (m_deviceInitialized)
            {
                try
                {
                    // Get up-to-date information on the device buffer.
                    UINT32 bufferFrames, bufferPadding;
                    ThrowIfFailed(m_device.audioClient->GetBufferSize(&bufferFrames));
                    ThrowIfFailed(m_device.audioClient->GetCurrentPadding(&bufferPadding));

                    // Find out how many frames we can write this time.
                    const UINT32 doFrames = std::min(bufferFrames - bufferPadding, (UINT32)(chunkFrames - doneFrames));

                    if (doFrames == 0)
                        continue;

                    // Write frames to the device buffer.
                    BYTE* deviceBuffer;
                    ThrowIfFailed(m_device.audioRenderClient->GetBuffer(doFrames, &deviceBuffer));
                    assert(frameSize == (m_device.format.Format.wBitsPerSample / 8 * m_device.format.Format.nChannels));
                    memcpy(deviceBuffer, chunk.GetConstData() + doneFrames * frameSize, doFrames * frameSize);
                    ThrowIfFailed(m_device.audioRenderClient->ReleaseBuffer(doFrames, 0));

                    // If the buffer is fully filled, set the corresponding event.
                    (bufferPadding + doFrames == bufferFrames) ? m_bufferFilled.Set() : m_bufferFilled.Reset();

                    doneFrames += doFrames;
                    m_pushedFrames += doFrames;

                    continue;
                }
                catch (HRESULT)
                {
                    ClearDevice();
                }
            }

            assert(!m_deviceInitialized);
            if (m_state == State_Running &&
                m_graphClock->GetTime() + MILLISECONDS_TO_100NS_UNITS(20) > m_startTime + m_lastSampleEnd)
            {
                break;
            }
        }

        return true;
    }
}
<commit_msg>Add a safeguard<commit_after>#include "pch.h"
#include "AudioRenderer.h"

namespace SaneAudioRenderer
{
    namespace
    {
        DspChunk PreProcess(IMediaSample* pSample, const AM_SAMPLE2_PROPERTIES& sampleProps,
                            const WAVEFORMATEX& sampleFormat, REFERENCE_TIME previousSampleEndTime)
        {
            DspChunk chunk;

            // TODO: can gradually accumulate error - decide what to do with it

            auto timeToFrames = [&](REFERENCE_TIME time)
            {
                return (size_t)(time * sampleFormat.nSamplesPerSec / OneSecond);
            };

            if (sampleProps.tStop <= previousSampleEndTime)
            {
                // Drop the sample.
                assert(chunk.IsEmpty());
            }
            else if (sampleProps.tStart < previousSampleEndTime)
            {
                // Crop the sample.
                size_t cropFrames = timeToFrames(previousSampleEndTime - sampleProps.tStart);
                if (cropFrames > 0)
                {
                    size_t cropBytes = cropFrames * sampleFormat.nChannels * sampleFormat.wBitsPerSample / 8;

                    AM_SAMPLE2_PROPERTIES croppedSampleProps = sampleProps;
                    assert((int32_t)cropBytes < croppedSampleProps.lActual);
                    croppedSampleProps.pbBuffer += cropBytes;
                    croppedSampleProps.lActual -= (int32_t)cropBytes;

                    chunk = DspChunk(pSample, croppedSampleProps, sampleFormat);
                }
                else
                {
                    chunk = DspChunk(pSample, sampleProps, sampleFormat);
                }
            }
            else if (sampleProps.tStart > previousSampleEndTime)
            {
                // Zero-pad the sample.
                size_t extendFrames = timeToFrames(sampleProps.tStart - previousSampleEndTime);
                if (extendFrames > 0)
                {
                    DspChunk tempChunk(pSample, sampleProps, sampleFormat);

                    chunk = DspChunk(tempChunk.GetFormat(), tempChunk.GetChannelCount(),
                                     tempChunk.GetFrameCount() + extendFrames, tempChunk.GetRate());

                    size_t extendBytes = extendFrames * chunk.GetFrameSize();

                    assert(chunk.GetSize() == tempChunk.GetSize() + extendBytes);
                    ZeroMemory(chunk.GetData(), extendBytes);
                    memcpy(chunk.GetData() + extendBytes, tempChunk.GetConstData(), tempChunk.GetSize());
                }
                else
                {
                    chunk = DspChunk(pSample, sampleProps, sampleFormat);
                }
            }
            else
            {
                // Leave the sample untouched.
                chunk = DspChunk(pSample, sampleProps, sampleFormat);
            }

            return chunk;
        }
    }

    AudioRenderer::AudioRenderer(ISettings* pSettings, IMyClock* pClock, CAMEvent& bufferFilled, HRESULT& result)
        : m_deviceManager(result)
        , m_graphClock(pClock)
        , m_flush(TRUE/*manual reset*/)
        , m_dspVolume(*this)
        , m_dspBalance(*this)
        , m_bufferFilled(bufferFilled)
        , m_settings(pSettings)
    {
        if (FAILED(result))
            return;

        try
        {
            if (!m_settings || !m_graphClock)
                throw E_UNEXPECTED;

            if (static_cast<HANDLE>(m_flush) == NULL ||
                static_cast<HANDLE>(m_bufferFilled) == NULL)
            {
                throw E_OUTOFMEMORY;
            }
        }
        catch (HRESULT ex)
        {
            result = ex;
        }
    }

    AudioRenderer::~AudioRenderer()
    {
        // Just in case.
        if (m_state != State_Stopped)
            Stop();

        m_device = {};
    }

    bool AudioRenderer::Enqueue(IMediaSample* pSample, const AM_SAMPLE2_PROPERTIES& sampleProps)
    {
        DspChunk chunk;

        {
            CAutoLock objectLock(this);
            assert(m_state != State_Stopped);

            if (!m_deviceInitialized &&
                m_deviceManager.CreateDevice(m_device, m_inputFormat, !!m_settings->UseExclusiveMode()))
            {
                m_deviceInitialized = true;

                assert(m_inputFormatInitialized);
                InitializeProcessors();

                m_startOffset = m_lastSampleEnd;

                if (m_state == State_Running)
                    StartDevice();
            }

            try
            {
                chunk = PreProcess(pSample, sampleProps, m_inputFormat.Format, m_lastSampleEnd);

                if (chunk.IsEmpty())
                    return true;

                if (m_deviceInitialized)
                {
                    m_dspMatrix.Process(chunk);
                    m_dspRate.Process(chunk);
                    m_dspTempo.Process(chunk);
                    m_dspCrossfeed.Process(chunk);
                    m_dspVolume.Process(chunk);
                    m_dspBalance.Process(chunk);
                    m_dspLimiter.Process(chunk);
                    m_dspDither.Process(chunk);

                    DspChunk::ToFormat(m_device.dspFormat, chunk);
                }

                m_lastSampleEnd = sampleProps.tStop;
            }
            catch (std::bad_alloc&)
            {
                chunk = DspChunk();
                assert(chunk.IsEmpty());
            }
        }

        return Push(chunk);
    }

    bool AudioRenderer::Finish(bool blockUntilEnd)
    {
        DspChunk chunk;

        {
            CAutoLock objectLock(this);
            assert(m_state != State_Stopped);

            if (!m_deviceInitialized)
                blockUntilEnd = false;

            try
            {
                if (m_deviceInitialized)
                {
                    m_dspMatrix.Finish(chunk);
                    m_dspRate.Finish(chunk);
                    m_dspTempo.Finish(chunk);
                    m_dspCrossfeed.Finish(chunk);
                    m_dspVolume.Finish(chunk);
                    m_dspBalance.Finish(chunk);
                    m_dspLimiter.Finish(chunk);
                    m_dspDither.Finish(chunk);

                    DspChunk::ToFormat(m_device.dspFormat, chunk);
                }
            }
            catch (std::bad_alloc&)
            {
                chunk = DspChunk();
                assert(chunk.IsEmpty());
            }
        }

        auto doBlock = [this]
        {
            TimePeriodHelper timePeriodHelper(1);

            m_graphClock->UnslaveClockFromAudio();

            for (;;)
            {
                int64_t actual, target;

                {
                    CAutoLock objectLock(this);

                    if (!m_deviceInitialized)
                        return true;

                    UINT64 deviceClockFrequency, deviceClockPosition;

                    try
                    {
                        ThrowIfFailed(m_device.audioClock->GetFrequency(&deviceClockFrequency));
                        ThrowIfFailed(m_device.audioClock->GetPosition(&deviceClockPosition, nullptr));
                    }
                    catch (HRESULT)
                    {
                        ClearDevice();
                        return true;
                    }

                    actual = llMulDiv(deviceClockPosition, OneSecond, deviceClockFrequency, 0);
                    target = llMulDiv(m_pushedFrames, OneSecond, m_device.format.Format.nSamplesPerSec, 0);

                    if (actual >= target)
                    {
                        assert(actual == target);
                        break;
                    }
                }

                if (m_flush.Wait(std::max(1, (int32_t)((target - actual) * 1000 / OneSecond))))
                    return false;
            }

            return true;
        };

        return Push(chunk) && (!blockUntilEnd || doBlock());
    }

    void AudioRenderer::BeginFlush()
    {
        m_flush.Set();
    }

    void AudioRenderer::EndFlush()
    {
        CAutoLock objectLock(this);
        assert(m_state != State_Running);

        if (m_deviceInitialized)
        {
            m_device.audioClient->Reset();
            m_bufferFilled.Reset();
        }

        m_flush.Reset();

        m_pushedFrames = 0;
    }

    void AudioRenderer::SetFormat(const WAVEFORMATEX& inputFormat)
    {
        CAutoLock objectLock(this);

        m_inputFormat = inputFormat.wFormatTag == WAVE_FORMAT_EXTENSIBLE ?
                            reinterpret_cast<const WAVEFORMATEXTENSIBLE&>(inputFormat) :
                            WAVEFORMATEXTENSIBLE{inputFormat};

        m_inputFormatInitialized = true;

        ClearDevice();
    }

    void AudioRenderer::NewSegment(double rate)
    {
        CAutoLock objectLock(this);

        m_startOffset = 0;
        m_lastSampleEnd = 0;
        m_rate = rate;

        assert(m_inputFormatInitialized);
        if (m_deviceInitialized)
            InitializeProcessors();
    }

    void AudioRenderer::Play(REFERENCE_TIME startTime)
    {
        CAutoLock objectLock(this);
        assert(m_state != State_Running);
        m_state = State_Running;

        m_startTime = startTime;
        StartDevice();
    }

    void AudioRenderer::Pause()
    {
        CAutoLock objectLock(this);
        m_state = State_Paused;

        if (m_deviceInitialized)
        {
            m_graphClock->UnslaveClockFromAudio();
            m_device.audioClient->Stop();
        }
    }

    void AudioRenderer::Stop()
    {
        CAutoLock objectLock(this);
        m_state = State_Stopped;

        ClearDevice();
    }

    std::unique_ptr<WAVEFORMATEXTENSIBLE> AudioRenderer::GetInputFormat()
    {
        CAutoLock objectLock(this);

        if (!m_inputFormatInitialized)
            return nullptr;

        return std::make_unique<WAVEFORMATEXTENSIBLE>(m_inputFormat);
    }

    std::unique_ptr<AudioDevice> AudioRenderer::GetDeviceFormat()
    {
        CAutoLock objectLock(this);

        if (!m_deviceInitialized)
            return nullptr;

        return std::make_unique<AudioDevice>(m_device);
    }

    void AudioRenderer::StartDevice()
    {
        CAutoLock objectLock(this);
        assert(m_state == State_Running);

        if (m_deviceInitialized)
        {
            m_graphClock->SlaveClockToAudio(m_device.audioClock, m_startTime + m_startOffset);
            m_startOffset = 0;
            m_device.audioClient->Start();
            //assert(m_bufferFilled.Check());
        }
    }

    void AudioRenderer::ClearDevice()
    {
        CAutoLock objectLock(this);

        if (m_deviceInitialized)
        {
            m_graphClock->UnslaveClockFromAudio();
            m_device.audioClient->Stop();
            m_bufferFilled.Reset();
        }

        m_deviceInitialized = false;
        m_device = {};
        m_deviceManager.ReleaseDevice();
    }

    void AudioRenderer::InitializeProcessors()
    {
        CAutoLock objectLock(this);
        assert(m_inputFormatInitialized);
        assert(m_deviceInitialized);

        const auto inRate = m_inputFormat.Format.nSamplesPerSec;
        const auto inChannels = m_inputFormat.Format.nChannels;
        const auto inMask = DspMatrix::GetChannelMask(m_inputFormat);
        const auto outRate = m_device.format.Format.nSamplesPerSec;
        const auto outChannels = m_device.format.Format.nChannels;
        const auto outMask = DspMatrix::GetChannelMask(m_device.format);

        m_dspMatrix.Initialize(inChannels, inMask, outChannels, outMask);
        m_dspRate.Initialize(inRate, outRate, outChannels);
        m_dspTempo.Initialize((float)m_rate, outRate, outChannels);
        m_dspCrossfeed.Initialize(!!m_settings->UseStereoCrossfeed(), outRate, outChannels, outMask);
        m_dspVolume.Initialize(m_device.exclusive);
        m_dspLimiter.Initialize(outRate, m_device.exclusive);
        m_dspDither.Initialize(m_device.dspFormat);
    }

    bool AudioRenderer::Push(DspChunk& chunk)
    {
        const uint32_t frameSize = chunk.GetFrameSize();
        const size_t chunkFrames = chunk.GetFrameCount();

        bool firstIteration = true;
        for (size_t doneFrames = 0; doneFrames < chunkFrames;)
        {
            // The device buffer is full or almost full at the beginning of the second and subsequent iterations.
            // Sleep until the buffer may have significant amount of free space. Unless interrupted.
            if (!firstIteration && m_flush.Wait(m_device.bufferDuration / 4))
                return false;

            firstIteration = false;

            CAutoLock objectLock(this);

            assert(m_state != State_Stopped);

            if (m_deviceInitialized)
            {
                try
                {
                    // Get up-to-date information on the device buffer.
                    UINT32 bufferFrames, bufferPadding;
                    ThrowIfFailed(m_device.audioClient->GetBufferSize(&bufferFrames));
                    ThrowIfFailed(m_device.audioClient->GetCurrentPadding(&bufferPadding));

                    // Find out how many frames we can write this time.
                    const UINT32 doFrames = std::min(bufferFrames - bufferPadding, (UINT32)(chunkFrames - doneFrames));

                    if (doFrames == 0)
                        continue;

                    // Write frames to the device buffer.
                    BYTE* deviceBuffer;
                    ThrowIfFailed(m_device.audioRenderClient->GetBuffer(doFrames, &deviceBuffer));
                    assert(frameSize == (m_device.format.Format.wBitsPerSample / 8 * m_device.format.Format.nChannels));
                    memcpy(deviceBuffer, chunk.GetConstData() + doneFrames * frameSize, doFrames * frameSize);
                    ThrowIfFailed(m_device.audioRenderClient->ReleaseBuffer(doFrames, 0));

                    // If the buffer is fully filled, set the corresponding event.
                    (bufferPadding + doFrames == bufferFrames) ? m_bufferFilled.Set() : m_bufferFilled.Reset();

                    doneFrames += doFrames;
                    m_pushedFrames += doFrames;

                    continue;
                }
                catch (HRESULT)
                {
                    ClearDevice();
                }
            }

            assert(!m_deviceInitialized);
            if (m_state == State_Running &&
                m_graphClock->GetTime() + MILLISECONDS_TO_100NS_UNITS(20) > m_startTime + m_lastSampleEnd)
            {
                break;
            }
        }

        return true;
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/audio/audio_output_resampler.h"

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/compiler_specific.h"
#include "base/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/time.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_output_dispatcher_impl.h"
#include "media/audio/audio_output_proxy.h"
#include "media/audio/audio_util.h"
#include "media/audio/sample_rates.h"
#include "media/base/audio_pull_fifo.h"
#include "media/base/limits.h"
#include "media/base/multi_channel_resampler.h"

#if defined(OS_WIN)
#include "media/audio/win/audio_low_latency_output_win.h"
#endif

namespace media {

static void RecordStats(const AudioParameters& output_params) {
  UMA_HISTOGRAM_ENUMERATION(
      "Media.HardwareAudioBitsPerChannel", output_params.bits_per_sample(),
      limits::kMaxBitsPerSample);

  bool recorded_wasapi_config = false;
#if defined(OS_WIN)
  // TODO(dalecurtis): Since channel mixing is handled by the output device
  // right now and not by AudioOutputResampler, we need to query for hardware
  // channel information.  Remove once AOR handles this, http://crbug.com/138762
  if (IsWASAPISupported()) {
    recorded_wasapi_config = true;
    UMA_HISTOGRAM_ENUMERATION(
        "Media.HardwareAudioChannelLayout",
        WASAPIAudioOutputStream::HardwareChannelLayout(), CHANNEL_LAYOUT_MAX);
    UMA_HISTOGRAM_ENUMERATION(
        "Media.HardwareAudioChannelCount",
        WASAPIAudioOutputStream::HardwareChannelCount(), limits::kMaxChannels);
  }
#endif

  if (!recorded_wasapi_config) {
    UMA_HISTOGRAM_ENUMERATION(
        "Media.HardwareAudioChannelLayout", output_params.channel_layout(),
        CHANNEL_LAYOUT_MAX);
    UMA_HISTOGRAM_ENUMERATION(
        "Media.HardwareAudioChannelCount", output_params.channels(),
        limits::kMaxChannels);
  }

  AudioSampleRate asr = media::AsAudioSampleRate(output_params.sample_rate());
  if (asr != kUnexpectedAudioSampleRate) {
    UMA_HISTOGRAM_ENUMERATION(
        "Media.HardwareAudioSamplesPerSecond", asr, kUnexpectedAudioSampleRate);
  } else {
    UMA_HISTOGRAM_COUNTS(
        "Media.HardwareAudioSamplesPerSecondUnexpected",
        output_params.sample_rate());
  }
}

AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager,
                                           const AudioParameters& input_params,
                                           const AudioParameters& output_params,
                                           const base::TimeDelta& close_delay)
    : AudioOutputDispatcher(audio_manager, input_params),
      source_callback_(NULL),
      io_ratio_(1),
      input_bytes_per_frame_(input_params.GetBytesPerFrame()),
      output_bytes_per_frame_(output_params.GetBytesPerFrame()),
      outstanding_audio_bytes_(0) {
  // TODO(dalecurtis): Add channel remixing.  http://crbug.com/138762
  DCHECK_EQ(input_params.channels(), output_params.channels());
  // Only resample or rebuffer if the input parameters don't match the output
  // parameters to avoid any unnecessary work.
  if (input_params.channels() != output_params.channels() ||
      input_params.sample_rate() != output_params.sample_rate() ||
      input_params.bits_per_sample() != output_params.bits_per_sample() ||
      input_params.frames_per_buffer() != output_params.frames_per_buffer()) {
    // Only resample if necessary since it's expensive.
    if (input_params.sample_rate() != output_params.sample_rate()) {
      DVLOG(1) << "Resampling from " << input_params.sample_rate() << " to "
               << output_params.sample_rate();
      double io_sample_rate_ratio = input_params.sample_rate() /
          static_cast<double>(output_params.sample_rate());
      // Include the I/O resampling ratio in our global I/O ratio.
      io_ratio_ *= io_sample_rate_ratio;
      resampler_.reset(new MultiChannelResampler(
          output_params.channels(), io_sample_rate_ratio, base::Bind(
              &AudioOutputResampler::ProvideInput, base::Unretained(this))));
    }

    // Include bits per channel differences.
    io_ratio_ *= static_cast<double>(input_params.bits_per_sample()) /
        output_params.bits_per_sample();

    // Include channel count differences.
    io_ratio_ *= static_cast<double>(input_params.channels()) /
        output_params.channels();

    // Since the resampler / output device may want a different buffer size than
    // the caller asked for, we need to use a FIFO to ensure that both sides
    // read in chunk sizes they're configured for.
    if (input_params.sample_rate() != output_params.sample_rate() ||
        input_params.frames_per_buffer() != output_params.frames_per_buffer()) {
      DVLOG(1) << "Rebuffering from " << input_params.frames_per_buffer()
               << " to " << output_params.frames_per_buffer();
      audio_fifo_.reset(new AudioPullFifo(
          input_params.channels(), input_params.frames_per_buffer(), base::Bind(
              &AudioOutputResampler::SourceCallback_Locked,
              base::Unretained(this))));
    }

    DVLOG(1) << "I/O ratio is " << io_ratio_;
  }

  // TODO(dalecurtis): All this code should be merged into AudioOutputMixer once
  // we've stabilized the issues there.
  dispatcher_ = new AudioOutputDispatcherImpl(
      audio_manager, output_params, close_delay);

  // Record UMA statistics for the hardware configuration.
  RecordStats(output_params);
}

AudioOutputResampler::~AudioOutputResampler() {}

bool AudioOutputResampler::OpenStream() {
  // TODO(dalecurtis): Automatically revert to high latency path if OpenStream()
  // fails; use default high latency output values + rebuffering / resampling.
  return dispatcher_->OpenStream();
}

bool AudioOutputResampler::StartStream(
    AudioOutputStream::AudioSourceCallback* callback,
    AudioOutputProxy* stream_proxy) {
  {
    base::AutoLock auto_lock(source_lock_);
    source_callback_ = callback;
  }
  return dispatcher_->StartStream(this, stream_proxy);
}

void AudioOutputResampler::StreamVolumeSet(AudioOutputProxy* stream_proxy,
                                           double volume) {
  dispatcher_->StreamVolumeSet(stream_proxy, volume);
}

void AudioOutputResampler::Reset() {
  base::AutoLock auto_lock(source_lock_);
  source_callback_ = NULL;
  outstanding_audio_bytes_ = 0;
  if (audio_fifo_.get())
    audio_fifo_->Clear();
  if (resampler_.get())
    resampler_->Flush();
}

void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) {
  Reset();
  dispatcher_->StopStream(stream_proxy);
}

void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) {
  Reset();
  dispatcher_->CloseStream(stream_proxy);
}

void AudioOutputResampler::Shutdown() {
  Reset();
  dispatcher_->Shutdown();
}

int AudioOutputResampler::OnMoreData(AudioBus* dest,
                                     AudioBuffersState buffers_state) {
  return OnMoreIOData(NULL, dest, buffers_state);
}

int AudioOutputResampler::OnMoreIOData(AudioBus* source,
                                       AudioBus* dest,
                                       AudioBuffersState buffers_state) {
  base::AutoLock auto_lock(source_lock_);
  // While we waited for |source_lock_| the callback might have been cleared.
  if (!source_callback_) {
    dest->Zero();
    return dest->frames();
  }

  current_buffers_state_ = buffers_state;

  if (!resampler_.get() && !audio_fifo_.get()) {
    // We have no internal buffers, so clear any outstanding audio data.
    outstanding_audio_bytes_ = 0;
    SourceCallback_Locked(dest);
    return dest->frames();
  }

  if (resampler_.get())
    resampler_->Resample(dest, dest->frames());
  else
    ProvideInput(dest);

  // Calculate how much data is left in the internal FIFO and resampler buffers.
  outstanding_audio_bytes_ -= dest->frames() * output_bytes_per_frame_;
  // Due to rounding errors while multiplying against |io_ratio_|,
  // |outstanding_audio_bytes_| might (rarely) slip below zero.
  if (outstanding_audio_bytes_ < 0) {
    DLOG(ERROR) << "Outstanding audio bytes went negative! Value: "
                << outstanding_audio_bytes_;
    outstanding_audio_bytes_ = 0;
  }

  // Always return the full number of frames requested, ProvideInput() will pad
  // with silence if it wasn't able to acquire enough data.
  return dest->frames();
}

void AudioOutputResampler::SourceCallback_Locked(AudioBus* audio_bus) {
  source_lock_.AssertAcquired();

  // Adjust playback delay to include the state of the internal buffers used by
  // the resampler and/or the FIFO.  Since the sample rate and bits per channel
  // may be different, we need to scale this value appropriately.
  AudioBuffersState new_buffers_state;
  new_buffers_state.pending_bytes = io_ratio_ *
      (current_buffers_state_.total_bytes() + outstanding_audio_bytes_);

  // Retrieve data from the original callback.  Zero any unfilled frames.
  int frames = source_callback_->OnMoreData(audio_bus, new_buffers_state);
  if (frames < audio_bus->frames())
    audio_bus->ZeroFramesPartial(frames, audio_bus->frames() - frames);

  // Scale the number of frames we got back in terms of input bytes to output
  // bytes accordingly.
  outstanding_audio_bytes_ +=
      (audio_bus->frames() * input_bytes_per_frame_) / io_ratio_;
}

void AudioOutputResampler::ProvideInput(AudioBus* audio_bus) {
  audio_fifo_->Consume(audio_bus, audio_bus->frames());
}

void AudioOutputResampler::OnError(AudioOutputStream* stream, int code) {
  base::AutoLock auto_lock(source_lock_);
  if (source_callback_)
    source_callback_->OnError(stream, code);
}

void AudioOutputResampler::WaitTillDataReady() {
  base::AutoLock auto_lock(source_lock_);
  if (source_callback_ && !outstanding_audio_bytes_)
    source_callback_->WaitTillDataReady();
}

}  // namespace media
<commit_msg>Remove WASAPI channel count. Too much trouble for now.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/audio/audio_output_resampler.h"

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/compiler_specific.h"
#include "base/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/time.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_output_dispatcher_impl.h"
#include "media/audio/audio_output_proxy.h"
#include "media/audio/audio_util.h"
#include "media/audio/sample_rates.h"
#include "media/base/audio_pull_fifo.h"
#include "media/base/limits.h"
#include "media/base/multi_channel_resampler.h"

namespace media {

static void RecordStats(const AudioParameters& output_params) {
  UMA_HISTOGRAM_ENUMERATION(
      "Media.HardwareAudioBitsPerChannel", output_params.bits_per_sample(),
      limits::kMaxBitsPerSample);
  UMA_HISTOGRAM_ENUMERATION(
      "Media.HardwareAudioChannelLayout", output_params.channel_layout(),
      CHANNEL_LAYOUT_MAX);
  UMA_HISTOGRAM_ENUMERATION(
      "Media.HardwareAudioChannelCount", output_params.channels(),
      limits::kMaxChannels);

  AudioSampleRate asr = media::AsAudioSampleRate(output_params.sample_rate());
  if (asr != kUnexpectedAudioSampleRate) {
    UMA_HISTOGRAM_ENUMERATION(
        "Media.HardwareAudioSamplesPerSecond", asr, kUnexpectedAudioSampleRate);
  } else {
    UMA_HISTOGRAM_COUNTS(
        "Media.HardwareAudioSamplesPerSecondUnexpected",
        output_params.sample_rate());
  }
}

AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager,
                                           const AudioParameters& input_params,
                                           const AudioParameters& output_params,
                                           const base::TimeDelta& close_delay)
    : AudioOutputDispatcher(audio_manager, input_params),
      source_callback_(NULL),
      io_ratio_(1),
      input_bytes_per_frame_(input_params.GetBytesPerFrame()),
      output_bytes_per_frame_(output_params.GetBytesPerFrame()),
      outstanding_audio_bytes_(0) {
  // TODO(dalecurtis): Add channel remixing.  http://crbug.com/138762
  DCHECK_EQ(input_params.channels(), output_params.channels());
  // Only resample or rebuffer if the input parameters don't match the output
  // parameters to avoid any unnecessary work.
  if (input_params.channels() != output_params.channels() ||
      input_params.sample_rate() != output_params.sample_rate() ||
      input_params.bits_per_sample() != output_params.bits_per_sample() ||
      input_params.frames_per_buffer() != output_params.frames_per_buffer()) {
    // Only resample if necessary since it's expensive.
    if (input_params.sample_rate() != output_params.sample_rate()) {
      DVLOG(1) << "Resampling from " << input_params.sample_rate() << " to "
               << output_params.sample_rate();
      double io_sample_rate_ratio = input_params.sample_rate() /
          static_cast<double>(output_params.sample_rate());
      // Include the I/O resampling ratio in our global I/O ratio.
      io_ratio_ *= io_sample_rate_ratio;
      resampler_.reset(new MultiChannelResampler(
          output_params.channels(), io_sample_rate_ratio, base::Bind(
              &AudioOutputResampler::ProvideInput, base::Unretained(this))));
    }

    // Include bits per channel differences.
    io_ratio_ *= static_cast<double>(input_params.bits_per_sample()) /
        output_params.bits_per_sample();

    // Include channel count differences.
    io_ratio_ *= static_cast<double>(input_params.channels()) /
        output_params.channels();

    // Since the resampler / output device may want a different buffer size than
    // the caller asked for, we need to use a FIFO to ensure that both sides
    // read in chunk sizes they're configured for.
    if (input_params.sample_rate() != output_params.sample_rate() ||
        input_params.frames_per_buffer() != output_params.frames_per_buffer()) {
      DVLOG(1) << "Rebuffering from " << input_params.frames_per_buffer()
               << " to " << output_params.frames_per_buffer();
      audio_fifo_.reset(new AudioPullFifo(
          input_params.channels(), input_params.frames_per_buffer(), base::Bind(
              &AudioOutputResampler::SourceCallback_Locked,
              base::Unretained(this))));
    }

    DVLOG(1) << "I/O ratio is " << io_ratio_;
  }

  // TODO(dalecurtis): All this code should be merged into AudioOutputMixer once
  // we've stabilized the issues there.
  dispatcher_ = new AudioOutputDispatcherImpl(
      audio_manager, output_params, close_delay);

  // Record UMA statistics for the hardware configuration.
  RecordStats(output_params);
}

AudioOutputResampler::~AudioOutputResampler() {}

bool AudioOutputResampler::OpenStream() {
  // TODO(dalecurtis): Automatically revert to high latency path if OpenStream()
  // fails; use default high latency output values + rebuffering / resampling.
  return dispatcher_->OpenStream();
}

bool AudioOutputResampler::StartStream(
    AudioOutputStream::AudioSourceCallback* callback,
    AudioOutputProxy* stream_proxy) {
  {
    base::AutoLock auto_lock(source_lock_);
    source_callback_ = callback;
  }
  return dispatcher_->StartStream(this, stream_proxy);
}

void AudioOutputResampler::StreamVolumeSet(AudioOutputProxy* stream_proxy,
                                           double volume) {
  dispatcher_->StreamVolumeSet(stream_proxy, volume);
}

void AudioOutputResampler::Reset() {
  base::AutoLock auto_lock(source_lock_);
  source_callback_ = NULL;
  outstanding_audio_bytes_ = 0;
  if (audio_fifo_.get())
    audio_fifo_->Clear();
  if (resampler_.get())
    resampler_->Flush();
}

void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) {
  Reset();
  dispatcher_->StopStream(stream_proxy);
}

void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) {
  Reset();
  dispatcher_->CloseStream(stream_proxy);
}

void AudioOutputResampler::Shutdown() {
  Reset();
  dispatcher_->Shutdown();
}

int AudioOutputResampler::OnMoreData(AudioBus* dest,
                                     AudioBuffersState buffers_state) {
  return OnMoreIOData(NULL, dest, buffers_state);
}

int AudioOutputResampler::OnMoreIOData(AudioBus* source,
                                       AudioBus* dest,
                                       AudioBuffersState buffers_state) {
  base::AutoLock auto_lock(source_lock_);
  // While we waited for |source_lock_| the callback might have been cleared.
  if (!source_callback_) {
    dest->Zero();
    return dest->frames();
  }

  current_buffers_state_ = buffers_state;

  if (!resampler_.get() && !audio_fifo_.get()) {
    // We have no internal buffers, so clear any outstanding audio data.
    outstanding_audio_bytes_ = 0;
    SourceCallback_Locked(dest);
    return dest->frames();
  }

  if (resampler_.get())
    resampler_->Resample(dest, dest->frames());
  else
    ProvideInput(dest);

  // Calculate how much data is left in the internal FIFO and resampler buffers.
  outstanding_audio_bytes_ -= dest->frames() * output_bytes_per_frame_;
  // Due to rounding errors while multiplying against |io_ratio_|,
  // |outstanding_audio_bytes_| might (rarely) slip below zero.
  if (outstanding_audio_bytes_ < 0) {
    DLOG(ERROR) << "Outstanding audio bytes went negative! Value: "
                << outstanding_audio_bytes_;
    outstanding_audio_bytes_ = 0;
  }

  // Always return the full number of frames requested, ProvideInput() will pad
  // with silence if it wasn't able to acquire enough data.
  return dest->frames();
}

void AudioOutputResampler::SourceCallback_Locked(AudioBus* audio_bus) {
  source_lock_.AssertAcquired();

  // Adjust playback delay to include the state of the internal buffers used by
  // the resampler and/or the FIFO.  Since the sample rate and bits per channel
  // may be different, we need to scale this value appropriately.
  AudioBuffersState new_buffers_state;
  new_buffers_state.pending_bytes = io_ratio_ *
      (current_buffers_state_.total_bytes() + outstanding_audio_bytes_);

  // Retrieve data from the original callback.  Zero any unfilled frames.
  int frames = source_callback_->OnMoreData(audio_bus, new_buffers_state);
  if (frames < audio_bus->frames())
    audio_bus->ZeroFramesPartial(frames, audio_bus->frames() - frames);

  // Scale the number of frames we got back in terms of input bytes to output
  // bytes accordingly.
  outstanding_audio_bytes_ +=
      (audio_bus->frames() * input_bytes_per_frame_) / io_ratio_;
}

void AudioOutputResampler::ProvideInput(AudioBus* audio_bus) {
  audio_fifo_->Consume(audio_bus, audio_bus->frames());
}

void AudioOutputResampler::OnError(AudioOutputStream* stream, int code) {
  base::AutoLock auto_lock(source_lock_);
  if (source_callback_)
    source_callback_->OnError(stream, code);
}

void AudioOutputResampler::WaitTillDataReady() {
  base::AutoLock auto_lock(source_lock_);
  if (source_callback_ && !outstanding_audio_bytes_)
    source_callback_->WaitTillDataReady();
}

}  // namespace media
<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

// RUN: cat %s | %cling -I %S -Xclang -verify
// Test forwardDeclaration
.rawInput 1
int id(int) __attribute__((annotate("Def.h")));

class __attribute__((annotate("Def.h"))) C;

namespace N {
  void nested() __attribute__((annotate("Def.h")));
}

template <typename T> class __attribute__((annotate("Def.h"))) Gen;
template <> class __attribute__((annotate("Def.h"))) Gen<int> ;
template <> class __attribute__((annotate("Spc.h"))) Gen<float>;

template <typename T,typename U> class  __attribute__((annotate("Def.h"))) Partial;
template <typename T> class __attribute__((annotate("Spc.h"))) Partial<T,int>;

namespace std {
  template<typename T,typename A> class __attribute__((annotate("vector"))) vector;
  template<typename T,typename A> class __attribute__((annotate("list"))) list;
  template<typename K,typename T,typename C,typename A> class __attribute__((annotate("map"))) map;

  template<typename Ch,typename Tr,typename A> class basic_string;
  template<typename T> class char_traits;
  template<typename T> class allocator;
  typedef basic_string<char,std::char_traits<char>,std::allocator<char>> string __attribute__((annotate("string"))) ;
}

.rawInput 0

#include "Def.h"
#include "Spc.h"
#include <vector>
#include <list>
#include <map>
#include <string>
//expected-no-diagnostics
.q
<commit_msg>added a function and a trait util from stl<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

// RUN: cat %s | %cling -I %S -Xclang -verify
// Test forwardDeclaration
.rawInput 1
int id(int) __attribute__((annotate("Def.h")));

class __attribute__((annotate("Def.h"))) C;

namespace N {
  void nested() __attribute__((annotate("Def.h")));
}

template <typename T> class __attribute__((annotate("Def.h"))) Gen;
template <> class __attribute__((annotate("Def.h"))) Gen<int> ;
template <> class __attribute__((annotate("Spc.h"))) Gen<float>;

template <typename T,typename U> class  __attribute__((annotate("Def.h"))) Partial;
template <typename T> class __attribute__((annotate("Spc.h"))) Partial<T,int>;

namespace std {

  template <typename T,typename A> class __attribute__((annotate("vector"))) vector;
  template <typename T,typename A> class __attribute__((annotate("list"))) list;
  template <typename K,typename T,typename C,typename A> class __attribute__((annotate("map"))) map;

  template <typename Ch,typename Tr,typename A> class basic_string;
  template <typename T> class char_traits;
  template <typename T> class allocator;
  typedef basic_string<char,std::char_traits<char>,std::allocator<char>> string __attribute__((annotate("string"))) ;

  template <typename R> void sort(R,R) __attribute__((annotate("algorithm")));
  template <typename R,typename C> void sort(R,R,C) __attribute__((annotate("algorithm")));

  template< bool B, typename T> struct __attribute__((annotate("type_traits"))) enable_if;
}

.rawInput 0

#include "Def.h"
#include "Spc.h"
#include <vector>
#include <list>
#include <map>
#include <string>
#include <algorithm>
#include <type_traits>
//expected-no-diagnostics
.q
<|endoftext|>


<commit_before>#include <GL/freeglut.h>

#include "MandelbrotSet.h"

#define WIDTH 800

#define HEIGHT 600

MandelbrotSet* ms = new MandelbrotSet(WIDTH, HEIGHT);

void reshape(int width, int height) {
	ms->setWidth(width);
	ms->setHeight(height);
}

void display() {
	glClear(GL_COLOR_BUFFER_BIT);
	glBegin(GL_POINTS);
	glColor3f(1.0, 0.0, 0.0);
	glVertex2i(40, 40);
	glEnd();
	ms->calculate();
	glFlush();
}

int main(int argc, char** argv) {
	glutInit(&argc, argv);
//	glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE,
//	GLUT_ACTION_GLUTMAINLOOP_RETURNS);
	glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);

	glutInitWindowSize(WIDTH, HEIGHT);
	glutCreateWindow("Mandelbrot Set");
	gluOrtho2D(0.0, WIDTH, 0.0, HEIGHT);
	glClearColor(0.0, 1.0, 1.0, 1.0);

	glutReshapeFunc(reshape);
	glutDisplayFunc(display);
//	glutKeyboardFunc(keyboard);
//	glutMouseFunc(mouse);

	glutMainLoop();

	return EXIT_FAILURE;
}
<commit_msg>change window width and clear color<commit_after>#include <GL/freeglut.h>

#include "MandelbrotSet.h"

#define WIDTH 800

#define HEIGHT 600

MandelbrotSet* ms = new MandelbrotSet(WIDTH, HEIGHT);

void reshape(int width, int height) {
	ms->setWidth(width);
	ms->setHeight(height);
}

void display() {
	glClear(GL_COLOR_BUFFER_BIT);
	glBegin(GL_POINTS);
	glColor3f(1.0, 0.0, 0.0);
	glVertex2i(40, 40);
	glEnd();
	ms->calculate();
	glFlush();
}

int main(int argc, char** argv) {
	glutInit(&argc, argv);
//	glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE,
//	GLUT_ACTION_GLUTMAINLOOP_RETURNS);
	glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);

	glutInitWindowSize(WIDTH - 10, HEIGHT);
	glutCreateWindow("Mandelbrot Set");
	gluOrtho2D(0.0, WIDTH, 0.0, HEIGHT);
	glClearColor(0.0, 0.0, 0.0, 1.0);

//	glutReshapeFunc(reshape);
	glutDisplayFunc(display);
//	glutKeyboardFunc(keyboard);
//	glutMouseFunc(mouse);

	glutMainLoop();

	return EXIT_FAILURE;
}
<|endoftext|>