TV Input 2.0 JNI
test: mmm, cts bug: 227673740 Change-Id: I8ea16727f0dd437de9c9019bb2e8b88b911181a4
This commit is contained in:
@@ -31,6 +31,9 @@ cc_library_static {
|
||||
"BroadcastRadio/convert.cpp",
|
||||
"BroadcastRadio/regions.cpp",
|
||||
"stats/SurfaceFlingerPuller.cpp",
|
||||
"tvinput/BufferProducerThread.cpp",
|
||||
"tvinput/JTvInputHal.cpp",
|
||||
"tvinput/TvInputHal_hidl.cpp",
|
||||
"com_android_server_adb_AdbDebuggingManager.cpp",
|
||||
"com_android_server_am_BatteryStatsService.cpp",
|
||||
"com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp",
|
||||
@@ -149,6 +152,7 @@ cc_defaults {
|
||||
"libdataloader",
|
||||
"libincfs",
|
||||
"android.hardware.audio.common@2.0",
|
||||
"android.media.audio.common.types-V1-ndk",
|
||||
"android.hardware.broadcastradio@1.0",
|
||||
"android.hardware.broadcastradio@1.1",
|
||||
"android.hardware.contexthub@1.0",
|
||||
@@ -175,6 +179,7 @@ cc_defaults {
|
||||
"android.hardware.power.stats-V1-ndk",
|
||||
"android.hardware.thermal@1.0",
|
||||
"android.hardware.tv.input@1.0",
|
||||
"android.hardware.tv.input-V1-ndk",
|
||||
"android.hardware.vibrator-V2-cpp",
|
||||
"android.hardware.vibrator@1.0",
|
||||
"android.hardware.vibrator@1.1",
|
||||
@@ -195,6 +200,7 @@ cc_defaults {
|
||||
|
||||
static_libs: [
|
||||
"android.hardware.broadcastradio@common-utils-1x-lib",
|
||||
"libaidlcommonsupport",
|
||||
],
|
||||
|
||||
product_variables: {
|
||||
|
||||
@@ -18,577 +18,15 @@
|
||||
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#include "android_os_MessageQueue.h"
|
||||
#include "android_runtime/AndroidRuntime.h"
|
||||
#include "android_runtime/android_view_Surface.h"
|
||||
#include <nativehelper/JNIHelp.h>
|
||||
#include "jni.h"
|
||||
#include "tvinput/JTvInputHal.h"
|
||||
|
||||
#include <android/hardware/tv/input/1.0/ITvInputCallback.h>
|
||||
#include <android/hardware/tv/input/1.0/ITvInput.h>
|
||||
#include <android/hardware/tv/input/1.0/types.h>
|
||||
#include <gui/Surface.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/KeyedVector.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Looper.h>
|
||||
#include <utils/NativeHandle.h>
|
||||
#include <hardware/tv_input.h>
|
||||
|
||||
using ::android::hardware::audio::common::V2_0::AudioDevice;
|
||||
using ::android::hardware::tv::input::V1_0::ITvInput;
|
||||
using ::android::hardware::tv::input::V1_0::ITvInputCallback;
|
||||
using ::android::hardware::tv::input::V1_0::Result;
|
||||
using ::android::hardware::tv::input::V1_0::TvInputDeviceInfo;
|
||||
using ::android::hardware::tv::input::V1_0::TvInputEvent;
|
||||
using ::android::hardware::tv::input::V1_0::TvInputEventType;
|
||||
using ::android::hardware::tv::input::V1_0::TvInputType;
|
||||
using ::android::hardware::tv::input::V1_0::TvStreamConfig;
|
||||
using ::android::hardware::Return;
|
||||
using ::android::hardware::Void;
|
||||
using ::android::hardware::hidl_vec;
|
||||
using ::android::hardware::hidl_string;
|
||||
gTvInputHalClassInfoType gTvInputHalClassInfo;
|
||||
gTvStreamConfigClassInfoType gTvStreamConfigClassInfo;
|
||||
gTvStreamConfigBuilderClassInfoType gTvStreamConfigBuilderClassInfo;
|
||||
gTvInputHardwareInfoBuilderClassInfoType gTvInputHardwareInfoBuilderClassInfo;
|
||||
|
||||
namespace android {
|
||||
|
||||
static struct {
|
||||
jmethodID deviceAvailable;
|
||||
jmethodID deviceUnavailable;
|
||||
jmethodID streamConfigsChanged;
|
||||
jmethodID firstFrameCaptured;
|
||||
} gTvInputHalClassInfo;
|
||||
|
||||
static struct {
|
||||
jclass clazz;
|
||||
} gTvStreamConfigClassInfo;
|
||||
|
||||
static struct {
|
||||
jclass clazz;
|
||||
|
||||
jmethodID constructor;
|
||||
jmethodID streamId;
|
||||
jmethodID type;
|
||||
jmethodID maxWidth;
|
||||
jmethodID maxHeight;
|
||||
jmethodID generation;
|
||||
jmethodID build;
|
||||
} gTvStreamConfigBuilderClassInfo;
|
||||
|
||||
static struct {
|
||||
jclass clazz;
|
||||
|
||||
jmethodID constructor;
|
||||
jmethodID deviceId;
|
||||
jmethodID type;
|
||||
jmethodID hdmiPortId;
|
||||
jmethodID cableConnectionStatus;
|
||||
jmethodID audioType;
|
||||
jmethodID audioAddress;
|
||||
jmethodID build;
|
||||
} gTvInputHardwareInfoBuilderClassInfo;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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();
|
||||
|
||||
static JTvInputHal* createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper);
|
||||
|
||||
int addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface);
|
||||
int removeStream(int deviceId, int streamId);
|
||||
const hidl_vec<TvStreamConfig> getStreamConfigs(int deviceId);
|
||||
|
||||
void onDeviceAvailable(const TvInputDeviceInfo& info);
|
||||
void onDeviceUnavailable(int deviceId);
|
||||
void onStreamConfigurationsChanged(int deviceId, int cableConnectionStatus);
|
||||
void onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
class NotifyHandler : public MessageHandler {
|
||||
public:
|
||||
NotifyHandler(JTvInputHal* hal, const TvInputEvent& event);
|
||||
|
||||
virtual void handleMessage(const Message& message);
|
||||
|
||||
private:
|
||||
TvInputEvent mEvent;
|
||||
JTvInputHal* mHal;
|
||||
};
|
||||
|
||||
class TvInputCallback : public ITvInputCallback {
|
||||
public:
|
||||
explicit TvInputCallback(JTvInputHal* hal);
|
||||
Return<void> notify(const TvInputEvent& event) override;
|
||||
private:
|
||||
JTvInputHal* mHal;
|
||||
};
|
||||
|
||||
JTvInputHal(JNIEnv* env, jobject thiz, sp<ITvInput> tvInput, const sp<Looper>& looper);
|
||||
|
||||
Mutex mLock;
|
||||
Mutex mStreamLock;
|
||||
jweak mThiz;
|
||||
sp<Looper> mLooper;
|
||||
|
||||
KeyedVector<int, KeyedVector<int, Connection> > mConnections;
|
||||
|
||||
sp<ITvInput> mTvInput;
|
||||
sp<ITvInputCallback> mTvInputCallback;
|
||||
};
|
||||
|
||||
JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, sp<ITvInput> tvInput,
|
||||
const sp<Looper>& looper) {
|
||||
mThiz = env->NewWeakGlobalRef(thiz);
|
||||
mTvInput = tvInput;
|
||||
mLooper = looper;
|
||||
mTvInputCallback = new TvInputCallback(this);
|
||||
mTvInput->setCallback(mTvInputCallback);
|
||||
}
|
||||
|
||||
JTvInputHal::~JTvInputHal() {
|
||||
mTvInput->setCallback(nullptr);
|
||||
JNIEnv* env = AndroidRuntime::getJNIEnv();
|
||||
env->DeleteWeakGlobalRef(mThiz);
|
||||
mThiz = NULL;
|
||||
}
|
||||
|
||||
JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper) {
|
||||
// TODO(b/31632518)
|
||||
sp<ITvInput> tvInput = ITvInput::getService();
|
||||
if (tvInput == nullptr) {
|
||||
ALOGE("Couldn't get tv.input service.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new JTvInputHal(env, thiz, tvInput, looper);
|
||||
}
|
||||
|
||||
int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface) {
|
||||
Mutex::Autolock autoLock(&mStreamLock);
|
||||
KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
|
||||
if (connections.indexOfKey(streamId) < 0) {
|
||||
connections.add(streamId, Connection());
|
||||
}
|
||||
Connection& connection = connections.editValueFor(streamId);
|
||||
if (connection.mSurface == surface) {
|
||||
// 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();
|
||||
}
|
||||
if (connection.mSourceHandle == NULL && connection.mThread == NULL) {
|
||||
// Need to configure stream
|
||||
Result result = Result::UNKNOWN;
|
||||
hidl_vec<TvStreamConfig> list;
|
||||
mTvInput->getStreamConfigurations(deviceId,
|
||||
[&result, &list](Result res, hidl_vec<TvStreamConfig> configs) {
|
||||
result = res;
|
||||
if (res == Result::OK) {
|
||||
list = configs;
|
||||
}
|
||||
});
|
||||
if (result != Result::OK) {
|
||||
ALOGE("Couldn't get stream configs for device id:%d result:%d", deviceId, result);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
int configIndex = -1;
|
||||
for (size_t i = 0; i < list.size(); ++i) {
|
||||
if (list[i].streamId == streamId) {
|
||||
configIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (configIndex == -1) {
|
||||
ALOGE("Cannot find a config with given stream ID: %d", streamId);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
connection.mStreamType = TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE;
|
||||
|
||||
result = Result::UNKNOWN;
|
||||
const native_handle_t* sidebandStream;
|
||||
mTvInput->openStream(deviceId, streamId,
|
||||
[&result, &sidebandStream](Result res, const native_handle_t* handle) {
|
||||
result = res;
|
||||
if (res == Result::OK) {
|
||||
if (handle) {
|
||||
sidebandStream = native_handle_clone(handle);
|
||||
} else {
|
||||
result = Result::UNKNOWN;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (result != Result::OK) {
|
||||
ALOGE("Couldn't open stream. device id:%d stream id:%d result:%d", deviceId, streamId,
|
||||
result);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
connection.mSourceHandle = NativeHandle::create((native_handle_t*)sidebandStream, true);
|
||||
}
|
||||
connection.mSurface = surface;
|
||||
if (connection.mSurface != nullptr) {
|
||||
connection.mSurface->setSidebandStream(connection.mSourceHandle);
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
int JTvInputHal::removeStream(int deviceId, int streamId) {
|
||||
Mutex::Autolock autoLock(&mStreamLock);
|
||||
KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
|
||||
if (connections.indexOfKey(streamId) < 0) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
Connection& connection = connections.editValueFor(streamId);
|
||||
if (connection.mSurface == NULL) {
|
||||
// Nothing to do
|
||||
return NO_ERROR;
|
||||
}
|
||||
if (Surface::isValid(connection.mSurface)) {
|
||||
connection.mSurface->setSidebandStream(NULL);
|
||||
}
|
||||
connection.mSurface.clear();
|
||||
if (connection.mThread != NULL) {
|
||||
connection.mThread->shutdown();
|
||||
connection.mThread.clear();
|
||||
}
|
||||
if (mTvInput->closeStream(deviceId, streamId) != Result::OK) {
|
||||
ALOGE("Couldn't close stream. device id:%d stream id:%d", deviceId, streamId);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
if (connection.mSourceHandle != NULL) {
|
||||
connection.mSourceHandle.clear();
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
const hidl_vec<TvStreamConfig> JTvInputHal::getStreamConfigs(int deviceId) {
|
||||
Result result = Result::UNKNOWN;
|
||||
hidl_vec<TvStreamConfig> list;
|
||||
mTvInput->getStreamConfigurations(deviceId,
|
||||
[&result, &list](Result res, hidl_vec<TvStreamConfig> configs) {
|
||||
result = res;
|
||||
if (res == Result::OK) {
|
||||
list = configs;
|
||||
}
|
||||
});
|
||||
if (result != Result::OK) {
|
||||
ALOGE("Couldn't get stream configs for device id:%d result:%d", deviceId, result);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void JTvInputHal::onDeviceAvailable(const TvInputDeviceInfo& info) {
|
||||
{
|
||||
Mutex::Autolock autoLock(&mLock);
|
||||
mConnections.add(info.deviceId, KeyedVector<int, Connection>());
|
||||
}
|
||||
JNIEnv* env = AndroidRuntime::getJNIEnv();
|
||||
|
||||
jobject builder = env->NewObject(
|
||||
gTvInputHardwareInfoBuilderClassInfo.clazz,
|
||||
gTvInputHardwareInfoBuilderClassInfo.constructor);
|
||||
env->CallObjectMethod(
|
||||
builder, gTvInputHardwareInfoBuilderClassInfo.deviceId, info.deviceId);
|
||||
env->CallObjectMethod(
|
||||
builder, gTvInputHardwareInfoBuilderClassInfo.type, info.type);
|
||||
if (info.type == TvInputType::HDMI) {
|
||||
env->CallObjectMethod(
|
||||
builder, gTvInputHardwareInfoBuilderClassInfo.hdmiPortId, info.portId);
|
||||
}
|
||||
env->CallObjectMethod(
|
||||
builder, gTvInputHardwareInfoBuilderClassInfo.cableConnectionStatus,
|
||||
info.cableConnectionStatus);
|
||||
env->CallObjectMethod(
|
||||
builder, gTvInputHardwareInfoBuilderClassInfo.audioType, info.audioType);
|
||||
if (info.audioType != AudioDevice::NONE) {
|
||||
uint8_t buffer[info.audioAddress.size() + 1];
|
||||
memcpy(buffer, info.audioAddress.data(), info.audioAddress.size());
|
||||
buffer[info.audioAddress.size()] = '\0';
|
||||
jstring audioAddress = env->NewStringUTF(reinterpret_cast<const char *>(buffer));
|
||||
env->CallObjectMethod(
|
||||
builder, gTvInputHardwareInfoBuilderClassInfo.audioAddress, audioAddress);
|
||||
env->DeleteLocalRef(audioAddress);
|
||||
}
|
||||
|
||||
jobject infoObject = env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.build);
|
||||
|
||||
env->CallVoidMethod(
|
||||
mThiz,
|
||||
gTvInputHalClassInfo.deviceAvailable,
|
||||
infoObject);
|
||||
|
||||
env->DeleteLocalRef(builder);
|
||||
env->DeleteLocalRef(infoObject);
|
||||
}
|
||||
|
||||
void JTvInputHal::onDeviceUnavailable(int deviceId) {
|
||||
{
|
||||
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,
|
||||
deviceId);
|
||||
}
|
||||
|
||||
void JTvInputHal::onStreamConfigurationsChanged(int deviceId, int cableConnectionStatus) {
|
||||
{
|
||||
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,
|
||||
cableConnectionStatus);
|
||||
}
|
||||
|
||||
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);
|
||||
if (seq == 0) {
|
||||
JNIEnv* env = AndroidRuntime::getJNIEnv();
|
||||
env->CallVoidMethod(
|
||||
mThiz,
|
||||
gTvInputHalClassInfo.firstFrameCaptured,
|
||||
deviceId,
|
||||
streamId);
|
||||
}
|
||||
}
|
||||
|
||||
JTvInputHal::NotifyHandler::NotifyHandler(JTvInputHal* hal, const TvInputEvent& event) {
|
||||
mHal = hal;
|
||||
mEvent = event;
|
||||
}
|
||||
|
||||
void JTvInputHal::NotifyHandler::handleMessage(const Message& message) {
|
||||
switch (mEvent.type) {
|
||||
case TvInputEventType::DEVICE_AVAILABLE: {
|
||||
mHal->onDeviceAvailable(mEvent.deviceInfo);
|
||||
} break;
|
||||
case TvInputEventType::DEVICE_UNAVAILABLE: {
|
||||
mHal->onDeviceUnavailable(mEvent.deviceInfo.deviceId);
|
||||
} break;
|
||||
case TvInputEventType::STREAM_CONFIGURATIONS_CHANGED: {
|
||||
int cableConnectionStatus = static_cast<int>(mEvent.deviceInfo.cableConnectionStatus);
|
||||
mHal->onStreamConfigurationsChanged(mEvent.deviceInfo.deviceId, cableConnectionStatus);
|
||||
} break;
|
||||
default:
|
||||
ALOGE("Unrecognizable event");
|
||||
}
|
||||
}
|
||||
|
||||
JTvInputHal::TvInputCallback::TvInputCallback(JTvInputHal* hal) {
|
||||
mHal = hal;
|
||||
}
|
||||
|
||||
Return<void> JTvInputHal::TvInputCallback::notify(const TvInputEvent& event) {
|
||||
mHal->mLooper->sendMessage(new NotifyHandler(mHal, event), static_cast<int>(event.type));
|
||||
return Void();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static jlong nativeOpen(JNIEnv* env, jobject thiz, jobject messageQueueObj) {
|
||||
sp<MessageQueue> messageQueue =
|
||||
android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
|
||||
@@ -617,7 +55,7 @@ static int nativeRemoveStream(JNIEnv* env, jclass clazz,
|
||||
static jobjectArray nativeGetStreamConfigs(JNIEnv* env, jclass clazz,
|
||||
jlong ptr, jint deviceId, jint generation) {
|
||||
JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
|
||||
const hidl_vec<TvStreamConfig> configs = tvInputHal->getStreamConfigs(deviceId);
|
||||
const std::vector<AidlTvStreamConfig> configs = tvInputHal->getStreamConfigs(deviceId);
|
||||
|
||||
jobjectArray result = env->NewObjectArray(configs.size(), gTvStreamConfigClassInfo.clazz, NULL);
|
||||
for (size_t i = 0; i < configs.size(); ++i) {
|
||||
|
||||
147
services/core/jni/tvinput/BufferProducerThread.cpp
Normal file
147
services/core/jni/tvinput/BufferProducerThread.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright 2022 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 "BufferProducerThread.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
56
services/core/jni/tvinput/BufferProducerThread.h
Normal file
56
services/core/jni/tvinput/BufferProducerThread.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2022 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gui/Surface.h>
|
||||
#include <hardware/tv_input.h>
|
||||
#include <utils/Thread.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
378
services/core/jni/tvinput/JTvInputHal.cpp
Normal file
378
services/core/jni/tvinput/JTvInputHal.cpp
Normal file
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
* Copyright 2022 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 "JTvInputHal.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, std::shared_ptr<ITvInputWrapper> tvInput,
|
||||
const sp<Looper>& looper) {
|
||||
mThiz = env->NewWeakGlobalRef(thiz);
|
||||
mTvInput = tvInput;
|
||||
mLooper = looper;
|
||||
mTvInputCallback = ::ndk::SharedRefBase::make<TvInputCallback>(this);
|
||||
mTvInput->setCallback(mTvInputCallback);
|
||||
}
|
||||
|
||||
JTvInputHal::~JTvInputHal() {
|
||||
mTvInput->setCallback(nullptr);
|
||||
JNIEnv* env = AndroidRuntime::getJNIEnv();
|
||||
env->DeleteWeakGlobalRef(mThiz);
|
||||
mThiz = NULL;
|
||||
}
|
||||
|
||||
JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper) {
|
||||
sp<HidlITvInput> hidlITvInput = HidlITvInput::getService();
|
||||
if (hidlITvInput != nullptr) {
|
||||
ALOGD("tv.input service is HIDL.");
|
||||
return new JTvInputHal(env, thiz,
|
||||
std::shared_ptr<ITvInputWrapper>(new ITvInputWrapper(hidlITvInput)),
|
||||
looper);
|
||||
}
|
||||
::ndk::SpAIBinder binder(AServiceManager_waitForService(TV_INPUT_AIDL_SERVICE_NAME));
|
||||
std::shared_ptr<AidlITvInput> aidlITvInput = AidlITvInput::fromBinder(binder);
|
||||
if (aidlITvInput == nullptr) {
|
||||
ALOGE("Couldn't get tv.input service.");
|
||||
return nullptr;
|
||||
}
|
||||
return new JTvInputHal(env, thiz,
|
||||
std::shared_ptr<ITvInputWrapper>(new ITvInputWrapper(aidlITvInput)),
|
||||
looper);
|
||||
}
|
||||
|
||||
int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface) {
|
||||
Mutex::Autolock autoLock(&mStreamLock);
|
||||
KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
|
||||
if (connections.indexOfKey(streamId) < 0) {
|
||||
connections.add(streamId, Connection());
|
||||
}
|
||||
Connection& connection = connections.editValueFor(streamId);
|
||||
if (connection.mSurface == surface) {
|
||||
// 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();
|
||||
}
|
||||
if (connection.mSourceHandle == NULL && connection.mThread == NULL) {
|
||||
// Need to configure stream
|
||||
::ndk::ScopedAStatus status;
|
||||
std::vector<AidlTvStreamConfig> list;
|
||||
status = mTvInput->getStreamConfigurations(deviceId, &list);
|
||||
if (!status.isOk()) {
|
||||
ALOGE("Couldn't get stream configs for device id:%d result:%d", deviceId,
|
||||
status.getServiceSpecificError());
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
int configIndex = -1;
|
||||
for (size_t i = 0; i < list.size(); ++i) {
|
||||
if (list[i].streamId == streamId) {
|
||||
configIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (configIndex == -1) {
|
||||
ALOGE("Cannot find a config with given stream ID: %d", streamId);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
connection.mStreamType = TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE;
|
||||
|
||||
AidlNativeHandle sidebandStream;
|
||||
status = mTvInput->openStream(deviceId, streamId, &sidebandStream);
|
||||
if (!status.isOk()) {
|
||||
ALOGE("Couldn't open stream. device id:%d stream id:%d result:%d", deviceId, streamId,
|
||||
status.getServiceSpecificError());
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
connection.mSourceHandle = NativeHandle::create(makeFromAidl(sidebandStream), true);
|
||||
}
|
||||
connection.mSurface = surface;
|
||||
if (connection.mSurface != nullptr) {
|
||||
connection.mSurface->setSidebandStream(connection.mSourceHandle);
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
int JTvInputHal::removeStream(int deviceId, int streamId) {
|
||||
Mutex::Autolock autoLock(&mStreamLock);
|
||||
KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
|
||||
if (connections.indexOfKey(streamId) < 0) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
Connection& connection = connections.editValueFor(streamId);
|
||||
if (connection.mSurface == NULL) {
|
||||
// Nothing to do
|
||||
return NO_ERROR;
|
||||
}
|
||||
if (Surface::isValid(connection.mSurface)) {
|
||||
connection.mSurface->setSidebandStream(NULL);
|
||||
}
|
||||
connection.mSurface.clear();
|
||||
if (connection.mThread != NULL) {
|
||||
connection.mThread->shutdown();
|
||||
connection.mThread.clear();
|
||||
}
|
||||
if (!mTvInput->closeStream(deviceId, streamId).isOk()) {
|
||||
ALOGE("Couldn't close stream. device id:%d stream id:%d", deviceId, streamId);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
if (connection.mSourceHandle != NULL) {
|
||||
connection.mSourceHandle.clear();
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
const std::vector<AidlTvStreamConfig> JTvInputHal::getStreamConfigs(int deviceId) {
|
||||
std::vector<AidlTvStreamConfig> list;
|
||||
::ndk::ScopedAStatus status = mTvInput->getStreamConfigurations(deviceId, &list);
|
||||
if (!status.isOk()) {
|
||||
ALOGE("Couldn't get stream configs for device id:%d result:%d", deviceId,
|
||||
status.getServiceSpecificError());
|
||||
return std::vector<AidlTvStreamConfig>();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void JTvInputHal::onDeviceAvailable(const TvInputDeviceInfoWrapper& info) {
|
||||
{
|
||||
Mutex::Autolock autoLock(&mLock);
|
||||
mConnections.add(info.deviceId, KeyedVector<int, Connection>());
|
||||
}
|
||||
JNIEnv* env = AndroidRuntime::getJNIEnv();
|
||||
jobject builder = env->NewObject(gTvInputHardwareInfoBuilderClassInfo.clazz,
|
||||
gTvInputHardwareInfoBuilderClassInfo.constructor);
|
||||
env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.deviceId, info.deviceId);
|
||||
env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.type, info.type);
|
||||
if (info.type == TvInputType::HDMI) {
|
||||
env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.hdmiPortId,
|
||||
info.portId);
|
||||
}
|
||||
env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.cableConnectionStatus,
|
||||
info.cableConnectionStatus);
|
||||
if (info.isHidl) {
|
||||
hidlSetUpAudioInfo(env, builder, info);
|
||||
} else {
|
||||
AidlAudioDeviceType audioType = info.aidlAudioDevice.type.type;
|
||||
env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.audioType, audioType);
|
||||
if (audioType != AidlAudioDeviceType::NONE) {
|
||||
std::stringstream ss;
|
||||
switch (info.aidlAudioDevice.address.getTag()) {
|
||||
case AidlAudioDeviceAddress::id:
|
||||
ss << info.aidlAudioDevice.address.get<AidlAudioDeviceAddress::id>();
|
||||
break;
|
||||
case AidlAudioDeviceAddress::mac: {
|
||||
std::vector<uint8_t> addrList =
|
||||
info.aidlAudioDevice.address.get<AidlAudioDeviceAddress::mac>();
|
||||
for (int i = 0; i < addrList.size(); i++) {
|
||||
if (i != 0) {
|
||||
ss << ':';
|
||||
}
|
||||
ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex
|
||||
<< static_cast<int32_t>(addrList[i]);
|
||||
}
|
||||
} break;
|
||||
case AidlAudioDeviceAddress::ipv4: {
|
||||
std::vector<uint8_t> addrList =
|
||||
info.aidlAudioDevice.address.get<AidlAudioDeviceAddress::ipv4>();
|
||||
for (int i = 0; i < addrList.size(); i++) {
|
||||
if (i != 0) {
|
||||
ss << '.';
|
||||
}
|
||||
ss << static_cast<int32_t>(addrList[i]);
|
||||
}
|
||||
} break;
|
||||
case AidlAudioDeviceAddress::ipv6: {
|
||||
std::vector<int32_t> addrList =
|
||||
info.aidlAudioDevice.address.get<AidlAudioDeviceAddress::ipv6>();
|
||||
for (int i = 0; i < addrList.size(); i++) {
|
||||
if (i != 0) {
|
||||
ss << ':';
|
||||
}
|
||||
ss << std::uppercase << std::setfill('0') << std::setw(4) << std::hex
|
||||
<< addrList[i];
|
||||
}
|
||||
} break;
|
||||
case AidlAudioDeviceAddress::alsa: {
|
||||
std::vector<int32_t> addrList =
|
||||
info.aidlAudioDevice.address.get<AidlAudioDeviceAddress::alsa>();
|
||||
ss << "card=" << addrList[0] << ";device=" << addrList[1];
|
||||
} break;
|
||||
}
|
||||
std::string bufferStr = ss.str();
|
||||
jstring audioAddress = env->NewStringUTF(bufferStr.c_str());
|
||||
env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.audioAddress,
|
||||
audioAddress);
|
||||
env->DeleteLocalRef(audioAddress);
|
||||
}
|
||||
}
|
||||
|
||||
jobject infoObject = env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.build);
|
||||
|
||||
env->CallVoidMethod(mThiz, gTvInputHalClassInfo.deviceAvailable, infoObject);
|
||||
|
||||
env->DeleteLocalRef(builder);
|
||||
env->DeleteLocalRef(infoObject);
|
||||
}
|
||||
|
||||
void JTvInputHal::onDeviceUnavailable(int deviceId) {
|
||||
{
|
||||
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, deviceId);
|
||||
}
|
||||
|
||||
void JTvInputHal::onStreamConfigurationsChanged(int deviceId, int cableConnectionStatus) {
|
||||
{
|
||||
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,
|
||||
cableConnectionStatus);
|
||||
}
|
||||
|
||||
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);
|
||||
if (seq == 0) {
|
||||
JNIEnv* env = AndroidRuntime::getJNIEnv();
|
||||
env->CallVoidMethod(mThiz, gTvInputHalClassInfo.firstFrameCaptured, deviceId, streamId);
|
||||
}
|
||||
}
|
||||
|
||||
JTvInputHal::TvInputDeviceInfoWrapper
|
||||
JTvInputHal::TvInputDeviceInfoWrapper::createDeviceInfoWrapper(
|
||||
const AidlTvInputDeviceInfo& aidlTvInputDeviceInfo) {
|
||||
TvInputDeviceInfoWrapper deviceInfo;
|
||||
deviceInfo.isHidl = false;
|
||||
deviceInfo.deviceId = aidlTvInputDeviceInfo.deviceId;
|
||||
deviceInfo.type = aidlTvInputDeviceInfo.type;
|
||||
deviceInfo.portId = aidlTvInputDeviceInfo.portId;
|
||||
deviceInfo.cableConnectionStatus = aidlTvInputDeviceInfo.cableConnectionStatus;
|
||||
deviceInfo.aidlAudioDevice = aidlTvInputDeviceInfo.audioDevice;
|
||||
return deviceInfo;
|
||||
}
|
||||
|
||||
JTvInputHal::TvInputEventWrapper JTvInputHal::TvInputEventWrapper::createEventWrapper(
|
||||
const AidlTvInputEvent& aidlTvInputEvent) {
|
||||
TvInputEventWrapper event;
|
||||
event.type = aidlTvInputEvent.type;
|
||||
event.deviceInfo =
|
||||
TvInputDeviceInfoWrapper::createDeviceInfoWrapper(aidlTvInputEvent.deviceInfo);
|
||||
return event;
|
||||
}
|
||||
|
||||
JTvInputHal::NotifyHandler::NotifyHandler(JTvInputHal* hal, const TvInputEventWrapper& event) {
|
||||
mHal = hal;
|
||||
mEvent = event;
|
||||
}
|
||||
|
||||
void JTvInputHal::NotifyHandler::handleMessage(const Message& message) {
|
||||
switch (mEvent.type) {
|
||||
case TvInputEventType::DEVICE_AVAILABLE: {
|
||||
mHal->onDeviceAvailable(mEvent.deviceInfo);
|
||||
} break;
|
||||
case TvInputEventType::DEVICE_UNAVAILABLE: {
|
||||
mHal->onDeviceUnavailable(mEvent.deviceInfo.deviceId);
|
||||
} break;
|
||||
case TvInputEventType::STREAM_CONFIGURATIONS_CHANGED: {
|
||||
int cableConnectionStatus = static_cast<int>(mEvent.deviceInfo.cableConnectionStatus);
|
||||
mHal->onStreamConfigurationsChanged(mEvent.deviceInfo.deviceId, cableConnectionStatus);
|
||||
} break;
|
||||
default:
|
||||
ALOGE("Unrecognizable event");
|
||||
}
|
||||
}
|
||||
|
||||
JTvInputHal::TvInputCallback::TvInputCallback(JTvInputHal* hal) {
|
||||
mHal = hal;
|
||||
}
|
||||
|
||||
::ndk::ScopedAStatus JTvInputHal::TvInputCallback::notify(const AidlTvInputEvent& event) {
|
||||
mHal->mLooper->sendMessage(new NotifyHandler(mHal,
|
||||
TvInputEventWrapper::createEventWrapper(event)),
|
||||
static_cast<int>(event.type));
|
||||
return ::ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
JTvInputHal::ITvInputWrapper::ITvInputWrapper(std::shared_ptr<AidlITvInput>& aidlTvInput)
|
||||
: mIsHidl(false), mAidlTvInput(aidlTvInput) {}
|
||||
|
||||
::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::setCallback(
|
||||
const std::shared_ptr<TvInputCallback>& in_callback) {
|
||||
if (mIsHidl) {
|
||||
return hidlSetCallback(in_callback);
|
||||
} else {
|
||||
return mAidlTvInput->setCallback(in_callback);
|
||||
}
|
||||
}
|
||||
|
||||
::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::getStreamConfigurations(
|
||||
int32_t in_deviceId, std::vector<AidlTvStreamConfig>* _aidl_return) {
|
||||
if (mIsHidl) {
|
||||
return hidlGetStreamConfigurations(in_deviceId, _aidl_return);
|
||||
} else {
|
||||
return mAidlTvInput->getStreamConfigurations(in_deviceId, _aidl_return);
|
||||
}
|
||||
}
|
||||
|
||||
::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::openStream(int32_t in_deviceId,
|
||||
int32_t in_streamId,
|
||||
AidlNativeHandle* _aidl_return) {
|
||||
if (mIsHidl) {
|
||||
return hidlOpenStream(in_deviceId, in_streamId, _aidl_return);
|
||||
} else {
|
||||
return mAidlTvInput->openStream(in_deviceId, in_streamId, _aidl_return);
|
||||
}
|
||||
}
|
||||
|
||||
::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::closeStream(int32_t in_deviceId,
|
||||
int32_t in_streamId) {
|
||||
if (mIsHidl) {
|
||||
return hidlCloseStream(in_deviceId, in_streamId);
|
||||
} else {
|
||||
return mAidlTvInput->closeStream(in_deviceId, in_streamId);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
185
services/core/jni/tvinput/JTvInputHal.h
Normal file
185
services/core/jni/tvinput/JTvInputHal.h
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright 2022 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define TV_INPUT_AIDL_SERVICE_NAME "android.hardware.tv.input.ITvInput/default"
|
||||
|
||||
#include <aidl/android/hardware/tv/input/BnTvInputCallback.h>
|
||||
#include <aidl/android/hardware/tv/input/CableConnectionStatus.h>
|
||||
#include <aidl/android/hardware/tv/input/ITvInput.h>
|
||||
#include <aidl/android/media/audio/common/AudioDevice.h>
|
||||
#include <aidlcommonsupport/NativeHandle.h>
|
||||
#include <android/binder_manager.h>
|
||||
#include <nativehelper/JNIHelp.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/KeyedVector.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Looper.h>
|
||||
#include <utils/NativeHandle.h>
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
#include "BufferProducerThread.h"
|
||||
#include "TvInputHal_hidl.h"
|
||||
#include "android_os_MessageQueue.h"
|
||||
#include "android_runtime/AndroidRuntime.h"
|
||||
#include "android_runtime/android_view_Surface.h"
|
||||
#include "tvinput/jstruct.h"
|
||||
|
||||
using ::aidl::android::hardware::tv::input::BnTvInputCallback;
|
||||
using ::aidl::android::hardware::tv::input::CableConnectionStatus;
|
||||
using ::aidl::android::hardware::tv::input::TvInputEventType;
|
||||
using ::aidl::android::hardware::tv::input::TvInputType;
|
||||
|
||||
using AidlAudioDevice = ::aidl::android::media::audio::common::AudioDevice;
|
||||
using AidlAudioDeviceAddress = ::aidl::android::media::audio::common::AudioDeviceAddress;
|
||||
using AidlAudioDeviceType = ::aidl::android::media::audio::common::AudioDeviceType;
|
||||
using AidlITvInput = ::aidl::android::hardware::tv::input::ITvInput;
|
||||
using AidlNativeHandle = ::aidl::android::hardware::common::NativeHandle;
|
||||
using AidlTvInputDeviceInfo = ::aidl::android::hardware::tv::input::TvInputDeviceInfo;
|
||||
using AidlTvInputEvent = ::aidl::android::hardware::tv::input::TvInputEvent;
|
||||
using AidlTvStreamConfig = ::aidl::android::hardware::tv::input::TvStreamConfig;
|
||||
|
||||
extern gTvInputHalClassInfoType gTvInputHalClassInfo;
|
||||
extern gTvStreamConfigClassInfoType gTvStreamConfigClassInfo;
|
||||
extern gTvStreamConfigBuilderClassInfoType gTvStreamConfigBuilderClassInfo;
|
||||
extern gTvInputHardwareInfoBuilderClassInfoType gTvInputHardwareInfoBuilderClassInfo;
|
||||
|
||||
namespace android {
|
||||
|
||||
class JTvInputHal {
|
||||
public:
|
||||
~JTvInputHal();
|
||||
|
||||
static JTvInputHal* createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper);
|
||||
|
||||
int addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface);
|
||||
int removeStream(int deviceId, int streamId);
|
||||
const std::vector<AidlTvStreamConfig> getStreamConfigs(int deviceId);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
class TvInputDeviceInfoWrapper {
|
||||
public:
|
||||
TvInputDeviceInfoWrapper() {}
|
||||
|
||||
static TvInputDeviceInfoWrapper createDeviceInfoWrapper(
|
||||
const AidlTvInputDeviceInfo& aidlTvInputDeviceInfo);
|
||||
static TvInputDeviceInfoWrapper createDeviceInfoWrapper(
|
||||
const HidlTvInputDeviceInfo& hidlTvInputDeviceInfo);
|
||||
|
||||
bool isHidl;
|
||||
int deviceId;
|
||||
TvInputType type;
|
||||
int portId;
|
||||
CableConnectionStatus cableConnectionStatus;
|
||||
AidlAudioDevice aidlAudioDevice;
|
||||
HidlAudioDevice hidlAudioType;
|
||||
::android::hardware::hidl_array<uint8_t, 32> hidlAudioAddress;
|
||||
};
|
||||
|
||||
class TvInputEventWrapper {
|
||||
public:
|
||||
TvInputEventWrapper() {}
|
||||
|
||||
static TvInputEventWrapper createEventWrapper(const AidlTvInputEvent& aidlTvInputEvent);
|
||||
static TvInputEventWrapper createEventWrapper(const HidlTvInputEvent& hidlTvInputEvent);
|
||||
|
||||
TvInputEventType type;
|
||||
TvInputDeviceInfoWrapper deviceInfo;
|
||||
};
|
||||
|
||||
class NotifyHandler : public MessageHandler {
|
||||
public:
|
||||
NotifyHandler(JTvInputHal* hal, const TvInputEventWrapper& event);
|
||||
|
||||
void handleMessage(const Message& message) override;
|
||||
|
||||
private:
|
||||
TvInputEventWrapper mEvent;
|
||||
JTvInputHal* mHal;
|
||||
};
|
||||
|
||||
class TvInputCallback : public HidlITvInputCallback, public BnTvInputCallback {
|
||||
public:
|
||||
explicit TvInputCallback(JTvInputHal* hal);
|
||||
::ndk::ScopedAStatus notify(const AidlTvInputEvent& event) override;
|
||||
Return<void> notify(const HidlTvInputEvent& event) override;
|
||||
|
||||
private:
|
||||
JTvInputHal* mHal;
|
||||
};
|
||||
|
||||
class ITvInputWrapper {
|
||||
public:
|
||||
ITvInputWrapper(std::shared_ptr<AidlITvInput>& aidlTvInput);
|
||||
ITvInputWrapper(sp<HidlITvInput>& hidlTvInput);
|
||||
|
||||
::ndk::ScopedAStatus setCallback(const std::shared_ptr<TvInputCallback>& in_callback);
|
||||
::ndk::ScopedAStatus getStreamConfigurations(int32_t in_deviceId,
|
||||
std::vector<AidlTvStreamConfig>* _aidl_return);
|
||||
::ndk::ScopedAStatus openStream(int32_t in_deviceId, int32_t in_streamId,
|
||||
AidlNativeHandle* _aidl_return);
|
||||
::ndk::ScopedAStatus closeStream(int32_t in_deviceId, int32_t in_streamId);
|
||||
|
||||
private:
|
||||
::ndk::ScopedAStatus hidlSetCallback(const std::shared_ptr<TvInputCallback>& in_callback);
|
||||
::ndk::ScopedAStatus hidlGetStreamConfigurations(
|
||||
int32_t in_deviceId, std::vector<AidlTvStreamConfig>* _aidl_return);
|
||||
::ndk::ScopedAStatus hidlOpenStream(int32_t in_deviceId, int32_t in_streamId,
|
||||
AidlNativeHandle* _aidl_return);
|
||||
::ndk::ScopedAStatus hidlCloseStream(int32_t in_deviceId, int32_t in_streamId);
|
||||
|
||||
bool mIsHidl;
|
||||
sp<HidlITvInput> mHidlTvInput;
|
||||
std::shared_ptr<AidlITvInput> mAidlTvInput;
|
||||
};
|
||||
|
||||
JTvInputHal(JNIEnv* env, jobject thiz, std::shared_ptr<ITvInputWrapper> tvInput,
|
||||
const sp<Looper>& looper);
|
||||
|
||||
void hidlSetUpAudioInfo(JNIEnv* env, jobject& builder, const TvInputDeviceInfoWrapper& info);
|
||||
void onDeviceAvailable(const TvInputDeviceInfoWrapper& info);
|
||||
void onDeviceUnavailable(int deviceId);
|
||||
void onStreamConfigurationsChanged(int deviceId, int cableConnectionStatus);
|
||||
void onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded);
|
||||
|
||||
Mutex mLock;
|
||||
Mutex mStreamLock;
|
||||
jweak mThiz;
|
||||
sp<Looper> mLooper;
|
||||
|
||||
KeyedVector<int, KeyedVector<int, Connection> > mConnections;
|
||||
|
||||
std::shared_ptr<ITvInputWrapper> mTvInput;
|
||||
std::shared_ptr<TvInputCallback> mTvInputCallback;
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
3
services/core/jni/tvinput/OWNERS
Normal file
3
services/core/jni/tvinput/OWNERS
Normal file
@@ -0,0 +1,3 @@
|
||||
include /media/java/android/media/tv/OWNERS
|
||||
|
||||
yixiaoluo@google.com
|
||||
138
services/core/jni/tvinput/TvInputHal_hidl.cpp
Normal file
138
services/core/jni/tvinput/TvInputHal_hidl.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright 2022 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 "JTvInputHal.h"
|
||||
|
||||
// Implement all HIDL related functions here.
|
||||
|
||||
namespace android {
|
||||
|
||||
void JTvInputHal::hidlSetUpAudioInfo(JNIEnv* env, jobject& builder,
|
||||
const TvInputDeviceInfoWrapper& info) {
|
||||
env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.audioType,
|
||||
info.hidlAudioType);
|
||||
if (info.hidlAudioType != HidlAudioDevice::NONE) {
|
||||
uint8_t buffer[info.hidlAudioAddress.size() + 1];
|
||||
memcpy(buffer, info.hidlAudioAddress.data(), info.hidlAudioAddress.size());
|
||||
buffer[info.hidlAudioAddress.size()] = '\0';
|
||||
jstring audioAddress = env->NewStringUTF(reinterpret_cast<const char*>(buffer));
|
||||
env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.audioAddress,
|
||||
audioAddress);
|
||||
env->DeleteLocalRef(audioAddress);
|
||||
}
|
||||
}
|
||||
|
||||
JTvInputHal::TvInputDeviceInfoWrapper
|
||||
JTvInputHal::TvInputDeviceInfoWrapper::createDeviceInfoWrapper(
|
||||
const HidlTvInputDeviceInfo& hidlTvInputDeviceInfo) {
|
||||
TvInputDeviceInfoWrapper deviceInfo;
|
||||
deviceInfo.isHidl = true;
|
||||
deviceInfo.deviceId = hidlTvInputDeviceInfo.deviceId;
|
||||
deviceInfo.type = TvInputType(static_cast<int32_t>(hidlTvInputDeviceInfo.type));
|
||||
deviceInfo.portId = hidlTvInputDeviceInfo.portId;
|
||||
deviceInfo.cableConnectionStatus = CableConnectionStatus(
|
||||
static_cast<int32_t>(hidlTvInputDeviceInfo.cableConnectionStatus));
|
||||
deviceInfo.hidlAudioType = hidlTvInputDeviceInfo.audioType;
|
||||
deviceInfo.hidlAudioAddress = hidlTvInputDeviceInfo.audioAddress;
|
||||
return deviceInfo;
|
||||
}
|
||||
|
||||
JTvInputHal::TvInputEventWrapper JTvInputHal::TvInputEventWrapper::createEventWrapper(
|
||||
const HidlTvInputEvent& hidlTvInputEvent) {
|
||||
TvInputEventWrapper event;
|
||||
event.type = TvInputEventType(static_cast<int32_t>(hidlTvInputEvent.type));
|
||||
event.deviceInfo =
|
||||
TvInputDeviceInfoWrapper::createDeviceInfoWrapper(hidlTvInputEvent.deviceInfo);
|
||||
return event;
|
||||
}
|
||||
|
||||
Return<void> JTvInputHal::TvInputCallback::notify(const HidlTvInputEvent& event) {
|
||||
mHal->mLooper->sendMessage(new NotifyHandler(mHal,
|
||||
TvInputEventWrapper::createEventWrapper(event)),
|
||||
static_cast<int>(event.type));
|
||||
return Void();
|
||||
}
|
||||
|
||||
JTvInputHal::ITvInputWrapper::ITvInputWrapper(sp<HidlITvInput>& hidlTvInput)
|
||||
: mIsHidl(true), mHidlTvInput(hidlTvInput) {}
|
||||
|
||||
::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::hidlSetCallback(
|
||||
const std::shared_ptr<TvInputCallback>& in_callback) {
|
||||
mHidlTvInput->setCallback(in_callback == nullptr ? nullptr
|
||||
: sp<TvInputCallback>(in_callback.get()));
|
||||
return ::ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::hidlGetStreamConfigurations(
|
||||
int32_t in_deviceId, std::vector<AidlTvStreamConfig>* _aidl_return) {
|
||||
Result result = Result::UNKNOWN;
|
||||
hidl_vec<HidlTvStreamConfig> list;
|
||||
mHidlTvInput->getStreamConfigurations(in_deviceId,
|
||||
[&result, &list](Result res,
|
||||
hidl_vec<HidlTvStreamConfig> configs) {
|
||||
result = res;
|
||||
if (res == Result::OK) {
|
||||
list = configs;
|
||||
}
|
||||
});
|
||||
if (result != Result::OK) {
|
||||
ALOGE("Couldn't get stream configs for device id:%d result:%d", in_deviceId, result);
|
||||
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(result));
|
||||
}
|
||||
for (size_t i = 0; i < list.size(); ++i) {
|
||||
AidlTvStreamConfig config;
|
||||
config.streamId = list[i].streamId;
|
||||
config.maxVideoHeight = list[i].maxVideoHeight;
|
||||
config.maxVideoWidth = list[i].maxVideoWidth;
|
||||
_aidl_return->push_back(config);
|
||||
}
|
||||
return ::ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::hidlOpenStream(int32_t in_deviceId,
|
||||
int32_t in_streamId,
|
||||
AidlNativeHandle* _aidl_return) {
|
||||
Result result = Result::UNKNOWN;
|
||||
native_handle_t* sidebandStream;
|
||||
mHidlTvInput->openStream(in_deviceId, in_streamId,
|
||||
[&result, &sidebandStream](Result res, const native_handle_t* handle) {
|
||||
result = res;
|
||||
if (res == Result::OK) {
|
||||
if (handle) {
|
||||
sidebandStream = native_handle_clone(handle);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (result != Result::OK) {
|
||||
ALOGE("Couldn't open stream. device id:%d stream id:%d result:%d", in_deviceId, in_streamId,
|
||||
result);
|
||||
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(result));
|
||||
}
|
||||
*_aidl_return = makeToAidl(sidebandStream);
|
||||
native_handle_delete(sidebandStream);
|
||||
return ::ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::hidlCloseStream(int32_t in_deviceId,
|
||||
int32_t in_streamId) {
|
||||
Result result = mHidlTvInput->closeStream(in_deviceId, in_streamId);
|
||||
if (result != Result::OK) {
|
||||
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(result));
|
||||
}
|
||||
return ::ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
35
services/core/jni/tvinput/TvInputHal_hidl.h
Normal file
35
services/core/jni/tvinput/TvInputHal_hidl.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2022 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Include all HIDL related files/names here.
|
||||
|
||||
#include <android/hardware/tv/input/1.0/ITvInput.h>
|
||||
#include <android/hardware/tv/input/1.0/ITvInputCallback.h>
|
||||
#include <android/hardware/tv/input/1.0/types.h>
|
||||
|
||||
using ::android::hardware::hidl_vec;
|
||||
using ::android::hardware::Return;
|
||||
using ::android::hardware::Void;
|
||||
using ::android::hardware::tv::input::V1_0::Result;
|
||||
|
||||
using HidlAudioDevice = ::android::hardware::audio::common::V2_0::AudioDevice;
|
||||
using HidlITvInput = ::android::hardware::tv::input::V1_0::ITvInput;
|
||||
using HidlITvInputCallback = ::android::hardware::tv::input::V1_0::ITvInputCallback;
|
||||
using HidlTvInputDeviceInfo = ::android::hardware::tv::input::V1_0::TvInputDeviceInfo;
|
||||
using HidlTvInputEvent = ::android::hardware::tv::input::V1_0::TvInputEvent;
|
||||
using HidlTvStreamConfig = ::android::hardware::tv::input::V1_0::TvStreamConfig;
|
||||
55
services/core/jni/tvinput/jstruct.h
Normal file
55
services/core/jni/tvinput/jstruct.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2022 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "jni.h"
|
||||
|
||||
typedef struct {
|
||||
jmethodID deviceAvailable;
|
||||
jmethodID deviceUnavailable;
|
||||
jmethodID streamConfigsChanged;
|
||||
jmethodID firstFrameCaptured;
|
||||
} gTvInputHalClassInfoType;
|
||||
|
||||
typedef struct {
|
||||
jclass clazz;
|
||||
} gTvStreamConfigClassInfoType;
|
||||
|
||||
typedef struct {
|
||||
jclass clazz;
|
||||
|
||||
jmethodID constructor;
|
||||
jmethodID streamId;
|
||||
jmethodID type;
|
||||
jmethodID maxWidth;
|
||||
jmethodID maxHeight;
|
||||
jmethodID generation;
|
||||
jmethodID build;
|
||||
} gTvStreamConfigBuilderClassInfoType;
|
||||
|
||||
typedef struct {
|
||||
jclass clazz;
|
||||
|
||||
jmethodID constructor;
|
||||
jmethodID deviceId;
|
||||
jmethodID type;
|
||||
jmethodID hdmiPortId;
|
||||
jmethodID cableConnectionStatus;
|
||||
jmethodID audioType;
|
||||
jmethodID audioAddress;
|
||||
jmethodID build;
|
||||
} gTvInputHardwareInfoBuilderClassInfoType;
|
||||
Reference in New Issue
Block a user