Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0b2691b6 authored by Wonsik Kim's avatar Wonsik Kim
Browse files

Implement support for buffer producer profile

Bug: 15177117
Change-Id: I7776a8b2959281f10cba742ab75f15f80f6feecf
parent 8845da77
Loading
Loading
Loading
Loading
+250 −25
Original line number Diff line number Diff line
@@ -68,6 +68,166 @@ static struct {

////////////////////////////////////////////////////////////////////////////////

class BufferProducerThread : public Thread {
public:
    BufferProducerThread(tv_input_device_t* device, int deviceId, const tv_stream_t* stream);

    virtual status_t readyToRun();

    void setSurface(const sp<Surface>& surface);
    void onCaptured(uint32_t seq, bool succeeded);
    void shutdown();

private:
    Mutex mLock;
    Condition mCondition;
    sp<Surface> mSurface;
    tv_input_device_t* mDevice;
    int mDeviceId;
    tv_stream_t mStream;
    sp<ANativeWindowBuffer_t> mBuffer;
    enum {
        CAPTURING,
        CAPTURED,
        RELEASED,
    } mBufferState;
    uint32_t mSeq;
    bool mShutdown;

    virtual bool threadLoop();

    void setSurfaceLocked(const sp<Surface>& surface);
};

BufferProducerThread::BufferProducerThread(
        tv_input_device_t* device, int deviceId, const tv_stream_t* stream)
    : Thread(false),
      mDevice(device),
      mDeviceId(deviceId),
      mBuffer(NULL),
      mBufferState(RELEASED),
      mSeq(0u),
      mShutdown(false) {
    memcpy(&mStream, stream, sizeof(mStream));
}

status_t BufferProducerThread::readyToRun() {
    sp<ANativeWindow> anw(mSurface);
    status_t err = native_window_set_usage(anw.get(), mStream.buffer_producer.usage);
    if (err != NO_ERROR) {
        return err;
    }
    err = native_window_set_buffers_dimensions(
            anw.get(), mStream.buffer_producer.width, mStream.buffer_producer.height);
    if (err != NO_ERROR) {
        return err;
    }
    err = native_window_set_buffers_format(anw.get(), mStream.buffer_producer.format);
    if (err != NO_ERROR) {
        return err;
    }
    return NO_ERROR;
}

void BufferProducerThread::setSurface(const sp<Surface>& surface) {
    Mutex::Autolock autoLock(&mLock);
    setSurfaceLocked(surface);
}

void BufferProducerThread::setSurfaceLocked(const sp<Surface>& surface) {
    if (surface == mSurface) {
        return;
    }

    if (mBufferState == CAPTURING) {
        mDevice->cancel_capture(mDevice, mDeviceId, mStream.stream_id, mSeq);
    }
    while (mBufferState == CAPTURING) {
        status_t err = mCondition.waitRelative(mLock, s2ns(1));
        if (err != NO_ERROR) {
            ALOGE("error %d while wating for buffer state to change.", err);
            break;
        }
    }
    mBuffer.clear();
    mBufferState = RELEASED;

    mSurface = surface;
    mCondition.broadcast();
}

void BufferProducerThread::onCaptured(uint32_t seq, bool succeeded) {
    Mutex::Autolock autoLock(&mLock);
    if (seq != mSeq) {
        ALOGW("Incorrect sequence value: expected %u actual %u", mSeq, seq);
    }
    if (mBufferState != CAPTURING) {
        ALOGW("mBufferState != CAPTURING : instead %d", mBufferState);
    }
    if (succeeded) {
        mBufferState = CAPTURED;
    } else {
        mBuffer.clear();
        mBufferState = RELEASED;
    }
    mCondition.broadcast();
}

void BufferProducerThread::shutdown() {
    Mutex::Autolock autoLock(&mLock);
    mShutdown = true;
    setSurfaceLocked(NULL);
    requestExitAndWait();
}

bool BufferProducerThread::threadLoop() {
    Mutex::Autolock autoLock(&mLock);

    status_t err = NO_ERROR;
    if (mSurface == NULL) {
        err = mCondition.waitRelative(mLock, s2ns(1));
        // It's OK to time out here.
        if (err != NO_ERROR && err != TIMED_OUT) {
            ALOGE("error %d while wating for non-null surface to be set", err);
            return false;
        }
        return true;
    }
    sp<ANativeWindow> anw(mSurface);
    while (mBufferState == CAPTURING) {
        err = mCondition.waitRelative(mLock, s2ns(1));
        if (err != NO_ERROR) {
            ALOGE("error %d while wating for buffer state to change.", err);
            return false;
        }
    }
    if (mBufferState == CAPTURED && anw != NULL) {
        err = anw->queueBuffer(anw.get(), mBuffer.get(), -1);
        if (err != NO_ERROR) {
            ALOGE("error %d while queueing buffer to surface", err);
            return false;
        }
        mBuffer.clear();
        mBufferState = RELEASED;
    }
    if (mBuffer == NULL && !mShutdown && anw != NULL) {
        ANativeWindowBuffer_t* buffer = NULL;
        err = native_window_dequeue_buffer_and_wait(anw.get(), &buffer);
        if (err != NO_ERROR) {
            ALOGE("error %d while dequeueing buffer to surface", err);
            return false;
        }
        mBuffer = buffer;
        mBufferState = CAPTURING;
        mDevice->request_capture(mDevice, mDeviceId, mStream.stream_id,
                                 buffer->handle, ++mSeq);
    }

    return true;
}

////////////////////////////////////////////////////////////////////////////////

class JTvInputHal {
public:
    ~JTvInputHal();
@@ -79,12 +239,18 @@ public:
    const tv_stream_config_t* getStreamConfigs(int deviceId, int* numConfigs);

private:
    // Connection between a surface and a stream.
    class Connection {
    public:
        Connection() {}

        sp<Surface> mSurface;
        tv_stream_type_t mStreamType;

        // Only valid when mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE
        sp<NativeHandle> mSourceHandle;
        // Only valid when mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER
        sp<BufferProducerThread> mThread;
    };

    JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* dev);
@@ -95,7 +261,9 @@ private:
    void onDeviceAvailable(const tv_input_device_info_t& info);
    void onDeviceUnavailable(int deviceId);
    void onStreamConfigurationsChanged(int deviceId);
    void onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded);

    Mutex mLock;
    jweak mThiz;
    tv_input_device_t* mDevice;
    tv_input_callback_ops_t mCallback;
@@ -153,11 +321,16 @@ int JTvInputHal::addStream(int deviceId, int streamId, const sp<Surface>& surfac
        // Nothing to do
        return NO_ERROR;
    }
    // Clear the surface in the connection.
    if (connection.mSurface != NULL) {
        if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
            if (Surface::isValid(connection.mSurface)) {
                connection.mSurface->setSidebandStream(NULL);
            }
        }
        connection.mSurface.clear();
    }
    connection.mSurface = surface;
    if (connection.mSourceHandle == NULL) {
    if (connection.mSourceHandle == NULL && connection.mThread == NULL) {
        // Need to configure stream
        int numConfigs = 0;
        const tv_stream_config_t* configs = NULL;
@@ -177,22 +350,32 @@ int JTvInputHal::addStream(int deviceId, int streamId, const sp<Surface>& surfac
            ALOGE("Cannot find a config with given stream ID: %d", streamId);
            return BAD_VALUE;
        }
        // TODO: handle buffer producer profile.
        if (configs[configIndex].type !=
                TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
            ALOGE("Profiles other than independent video source is not yet "
                  "supported : type = %d", configs[configIndex].type);
            return INVALID_OPERATION;
        }
        tv_stream_t stream;
        stream.stream_id = configs[configIndex].stream_id;
        if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
            stream.buffer_producer.width = configs[configIndex].max_video_width;
            stream.buffer_producer.height = configs[configIndex].max_video_height;
        }
        if (mDevice->open_stream(mDevice, deviceId, &stream) != 0) {
            ALOGE("Couldn't add stream");
            return UNKNOWN_ERROR;
        }
        if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
            connection.mSourceHandle = NativeHandle::create(
                    stream.sideband_stream_source_handle, false);
        } else if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
            if (connection.mThread != NULL) {
                connection.mThread->shutdown();
            }
            connection.mThread = new BufferProducerThread(mDevice, deviceId, &stream);
            connection.mThread->run();
        }
    }
    connection.mSurface = surface;
    if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
        connection.mSurface->setSidebandStream(connection.mSourceHandle);
    } else if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
        connection.mThread->setSurface(surface);
    }
    return NO_ERROR;
}
@@ -220,6 +403,12 @@ int JTvInputHal::removeStream(int deviceId, int streamId) {
            ALOGE("Couldn't remove stream");
            return BAD_VALUE;
        }

        // Clear everything
        if (connection.mThread != NULL) {
            connection.mThread->shutdown();
            connection.mThread.clear();
        }
        connection.mSourceHandle.clear();
    }
    return NO_ERROR;
@@ -249,14 +438,29 @@ void JTvInputHal::notify(
        case TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED: {
            thiz->onStreamConfigurationsChanged(event->device_info.device_id);
        } break;
        case TV_INPUT_EVENT_CAPTURE_SUCCEEDED: {
            thiz->onCaptured(event->capture_result.device_id,
                             event->capture_result.stream_id,
                             event->capture_result.seq,
                             true /* succeeded */);
        } break;
        case TV_INPUT_EVENT_CAPTURE_FAILED: {
            thiz->onCaptured(event->capture_result.device_id,
                             event->capture_result.stream_id,
                             event->capture_result.seq,
                             false /* succeeded */);
        } break;
        default:
            ALOGE("Unrecognizable event");
    }
}

void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) {
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    {
        Mutex::Autolock autoLock(&mLock);
        mConnections.add(info.device_id, KeyedVector<int, Connection>());
    }
    JNIEnv* env = AndroidRuntime::getJNIEnv();

    jobject builder = env->NewObject(
            gTvInputHardwareInfoBuilderClassInfo.clazz,
@@ -290,13 +494,16 @@ void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) {
}

void JTvInputHal::onDeviceUnavailable(int deviceId) {
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    {
        Mutex::Autolock autoLock(&mLock);
        KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
        for (size_t i = 0; i < connections.size(); ++i) {
            removeStream(deviceId, connections.keyAt(i));
        }
        connections.clear();
        mConnections.removeItem(deviceId);
    }
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    env->CallVoidMethod(
            mThiz,
            gTvInputHalClassInfo.deviceUnavailable,
@@ -304,18 +511,36 @@ void JTvInputHal::onDeviceUnavailable(int deviceId) {
}

void JTvInputHal::onStreamConfigurationsChanged(int deviceId) {
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    {
        Mutex::Autolock autoLock(&mLock);
        KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
        for (size_t i = 0; i < connections.size(); ++i) {
            removeStream(deviceId, connections.keyAt(i));
        }
        connections.clear();
    }
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    env->CallVoidMethod(
            mThiz,
            gTvInputHalClassInfo.streamConfigsChanged,
            deviceId);
}

void JTvInputHal::onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded) {
    sp<BufferProducerThread> thread;
    {
        Mutex::Autolock autoLock(&mLock);
        KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
        Connection& connection = connections.editValueFor(streamId);
        if (connection.mThread == NULL) {
            ALOGE("capture thread not existing.");
            return;
        }
        thread = connection.mThread;
    }
    thread->onCaptured(seq, succeeded);
}

////////////////////////////////////////////////////////////////////////////////

static jlong nativeOpen(JNIEnv* env, jobject thiz) {