/*
* Copyright (C) 2012 The Android Open Source Project
*
* 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.
*/
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#include <utils/StrongPointer.h>
#include "Log.h"
#include "audio/Buffer.h"
#include "StringUtil.h"
#include "SimpleScriptExec.h"
#include "SignalProcessingImpl.h"
#include "task/TaskCase.h"
enum ToPythonCommandType {
EHeader = 0x0,
ETerminate = 0x1,
EFunctionName = 0x2,
EAudioMono = 0x4,
EAudioStereo = 0x5,
EValue64Int = 0x8,
EValueDouble = 0x9,
EExecutionResult = 0x10
};
const android::String8 \
SignalProcessingImpl::MAIN_PROCESSING_SCRIPT("test_description/processing_main.py");
SignalProcessingImpl::SignalProcessingImpl()
: mChildRunning(false),
mBuffer(1024)
{
}
SignalProcessingImpl::~SignalProcessingImpl()
{
if (mSocket.get() != NULL) {
int terminationCommand [] = {ETerminate, 0};
send((char*)terminationCommand, sizeof(terminationCommand));
mSocket->release();
}
if (mChildRunning) {
waitpid(mChildPid, NULL, 0);
}
}
#define CHILD_LOGE(x...) do { fprintf(stderr, x); \
fprintf(stderr, " %s - %d\n", __FILE__, __LINE__); } while(0)
const int CHILD_WAIT_TIME_US = 100000;
bool SignalProcessingImpl::init(const android::String8& script)
{
pid_t pid;
if ((pid = fork()) < 0) {
LOGE("SignalProcessingImpl::init fork failed %d", errno);
return false;
} else if (pid == 0) { // child
if (execl(SimpleScriptExec::PYTHON_PATH, SimpleScriptExec::PYTHON_PATH,
script.string(), NULL) < 0) {
CHILD_LOGE("execl %s %s failed %d", SimpleScriptExec::PYTHON_PATH,
script.string(), errno);
exit(EXIT_FAILURE);
}
} else { // parent
mChildPid = pid;
mChildRunning = true;
int result = false;
int retryCount = 0;
// not that clean, but it takes some time for python side to have socket ready
const int MAX_RETRY = 20;
while (retryCount < MAX_RETRY) {
usleep(CHILD_WAIT_TIME_US);
mSocket.reset(new ClientSocket());
if (mSocket.get() == NULL) {
result = false;
break;
}
if (mSocket->init("127.0.0.1", SCRIPT_PORT, true)) {
result = true;
break;
}
retryCount++;
}
if (!result) {
LOGE("cannot connect to child");
mSocket.reset(NULL);
return result;
}
}
return true;
}
TaskGeneric::ExecutionResult SignalProcessingImpl::run( const android::String8& functionScript,
int nInputs, bool* inputTypes, void** inputs,
int nOutputs, bool* outputTypes, void** outputs)
{
mBuffer.reset();
mBuffer.write <int32_t>((int32_t)EHeader);
mBuffer.write<int32_t>(nInputs + 1);
mBuffer.write<int32_t>((int32_t)EFunctionName);
mBuffer.write<int32_t>((int32_t)functionScript.length());
mBuffer.writeStr(functionScript);
if (!send(mBuffer.getBuffer(), mBuffer.getSizeWritten())) {
LOGE("send failed");
return TaskGeneric::EResultError;
}
for (int i = 0; i < nInputs; i++) {
mBuffer.reset();
if (inputTypes[i]) { // android::sp<Buffer>*
android::sp<Buffer>* buffer = reinterpret_cast<android::sp<Buffer>*>(inputs[i]);
mBuffer.write<int32_t>((int32_t)((*buffer)->isStereo() ? EAudioStereo : EAudioMono));
int dataLen = (*buffer)->getSize();
mBuffer.write<int32_t>(dataLen);
if (!send(mBuffer.getBuffer(), mBuffer.getSizeWritten())) {
LOGE("send failed");
return TaskGeneric::EResultError;
}
if (!send((*buffer)->getData(), dataLen)) {
LOGE("send failed");
return TaskGeneric::EResultError;
}
LOGD("%d-th param buffer %d, stereo:%d", dataLen, (*buffer)->isStereo());
} else { //TaskCase::Value*
TaskCase::Value* val = reinterpret_cast<TaskCase::Value*>(inputs[i]);
bool isI64 = (val->getType() == TaskCase::Value::ETypeI64);
mBuffer.write<int32_t>((int32_t)(isI64 ? EValue64Int : EValueDouble));
if (isI64) {
mBuffer.write<int64_t>(val->getInt64());
} else {
mBuffer.write<double>(val->getDouble());
}
if (!send(mBuffer.getBuffer(), mBuffer.getSizeWritten())) {
LOGE("send failed");
return TaskGeneric::EResultError;
}
LOGD("%d-th param Value", i);
}
}
int32_t header[4]; // id 0 - no of types - id 0x10 - ExecutionResult
if (!read((char*)header, sizeof(header))) {
LOGE("read failed");
return TaskGeneric::EResultError;
}
if (header[0] != 0) {
LOGE("wrong data");
return TaskGeneric::EResultError;
}
if (header[2] != EExecutionResult) {
LOGE("wrong data");
return TaskGeneric::EResultError;
}
if (header[3] == TaskGeneric::EResultError) {
LOGE("script returned error %d", header[3]);
return (TaskGeneric::ExecutionResult)header[3];
}
if ((header[1] - 1) != nOutputs) {
LOGE("wrong data");
return TaskGeneric::EResultError;
}
for (int i = 0; i < nOutputs; i++) {
int32_t type;
if (!read((char*)&type, sizeof(type))) {
LOGE("read failed");
return TaskGeneric::EResultError;
}
if (outputTypes[i]) { // android::sp<Buffer>*
int32_t dataLen;
if (!read((char*)&dataLen, sizeof(dataLen))) {
LOGE("read failed");
return TaskGeneric::EResultError;
}
android::sp<Buffer>* buffer = reinterpret_cast<android::sp<Buffer>*>(outputs[i]);
if (buffer->get() == NULL) { // data not allocated, this can happen for unknown-length output
*buffer = new Buffer(dataLen, dataLen, (type == EAudioStereo) ? true: false);
if (buffer->get() == NULL) {
LOGE("alloc failed");
return TaskGeneric::EResultError;
}
}
bool isStereo = (*buffer)->isStereo();
if (((type == EAudioStereo) && isStereo) || ((type == EAudioMono) && !isStereo)) {
// valid
} else {
LOGE("%d-th output wrong type %d stereo: %d", i, type, isStereo);
return TaskGeneric::EResultError;
}
if (dataLen > (int)(*buffer)->getSize()) {
LOGE("%d-th output data too long %d while buffer size %d", i, dataLen,
(*buffer)->getSize());
return TaskGeneric::EResultError;
}
if (!read((*buffer)->getData(), dataLen)) {
LOGE("read failed");
return TaskGeneric::EResultError;
}
LOGD("received buffer %x %x", ((*buffer)->getData())[0], ((*buffer)->getData())[1]);
(*buffer)->setHandled(dataLen);
(*buffer)->setSize(dataLen);
} else { //TaskCase::Value*
TaskCase::Value* val = reinterpret_cast<TaskCase::Value*>(outputs[i]);
if ((type == EValue64Int) || (type == EValueDouble)) {
if (!read((char*)val->getPtr(), sizeof(int64_t))) {
LOGE("read failed");
return TaskGeneric::EResultError;
}
if (type == EValue64Int) {
val->setType(TaskCase::Value::ETypeI64);
} else {
val->setType(TaskCase::Value::ETypeDouble);
}
} else {
LOGE("wrong type %d", type);
return TaskGeneric::EResultError;
}
}
}
return (TaskGeneric::ExecutionResult)header[3];
}
bool SignalProcessingImpl::send(const char* data, int len)
{
//LOGD("send %d", len);
return mSocket->sendData(data, len);
}
bool SignalProcessingImpl::read(char* data, int len)
{
const int READ_TIMEOUT_MS = 60000 * 2; // as some calculation like calc_delay takes almost 20 secs
//LOGD("read %d", len);
return mSocket->readData(data, len, READ_TIMEOUT_MS);
}