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

Commit 1e47ff9f authored by Wonsik Kim's avatar Wonsik Kim Committed by Android (Google) Code Review
Browse files

Merge "Implement support for buffer producer profile"

parents 98d29489 0b2691b6
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) {