/* * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "webrtc/modules/video_render/android/video_render_android_impl.h" #include "webrtc/modules/video_render/video_render_internal.h" #include "webrtc/system_wrappers/include/critical_section_wrapper.h" #include "webrtc/system_wrappers/include/event_wrapper.h" #include "webrtc/system_wrappers/include/tick_util.h" #ifdef ANDROID #include #include #undef WEBRTC_TRACE #define WEBRTC_TRACE(a,b,c,...) __android_log_print(ANDROID_LOG_DEBUG, "*WEBRTCN*", __VA_ARGS__) #else #include "webrtc/system_wrappers/include/trace.h" #endif namespace webrtc { JavaVM* VideoRenderAndroid::g_jvm = NULL; int32_t SetRenderAndroidVM(JavaVM* javaVM) { WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, -1, "%s", __FUNCTION__); VideoRenderAndroid::g_jvm = javaVM; return 0; } VideoRenderAndroid::VideoRenderAndroid( const int32_t id, const VideoRenderType videoRenderType, void* window, const bool /*fullscreen*/): _id(id), _critSect(*CriticalSectionWrapper::CreateCriticalSection()), _renderType(videoRenderType), _ptrWindow((jobject)(window)), _javaShutDownFlag(false), _javaShutdownEvent(*EventWrapper::Create()), _javaRenderEvent(*EventWrapper::Create()), _lastJavaRenderEvent(0), _javaRenderJniEnv(NULL) { } VideoRenderAndroid::~VideoRenderAndroid() { WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "VideoRenderAndroid dtor"); if (_javaRenderThread) StopRender(); for (AndroidStreamMap::iterator it = _streamsMap.begin(); it != _streamsMap.end(); ++it) { delete it->second; } delete &_javaShutdownEvent; delete &_javaRenderEvent; delete &_critSect; } int32_t VideoRenderAndroid::ChangeWindow(void* /*window*/) { return -1; } VideoRenderCallback* VideoRenderAndroid::AddIncomingRenderStream(const uint32_t streamId, const uint32_t zOrder, const float left, const float top, const float right, const float bottom) { CriticalSectionScoped cs(&_critSect); AndroidStream* renderStream = NULL; AndroidStreamMap::iterator item = _streamsMap.find(streamId); if (item != _streamsMap.end() && item->second != NULL) { WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, -1, "%s: Render stream already exists", __FUNCTION__); return renderStream; } renderStream = CreateAndroidRenderChannel(streamId, zOrder, left, top, right, bottom, *this); if (renderStream) { _streamsMap[streamId] = renderStream; } else { WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "(%s:%d): renderStream is NULL", __FUNCTION__, __LINE__); return NULL; } return renderStream; } int32_t VideoRenderAndroid::DeleteIncomingRenderStream( const uint32_t streamId) { CriticalSectionScoped cs(&_critSect); AndroidStreamMap::iterator item = _streamsMap.find(streamId); if (item == _streamsMap.end()) { WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "(%s:%d): renderStream is NULL", __FUNCTION__, __LINE__); return -1; } delete item->second; _streamsMap.erase(item); return 0; } int32_t VideoRenderAndroid::GetIncomingRenderStreamProperties( const uint32_t streamId, uint32_t& zOrder, float& left, float& top, float& right, float& bottom) const { return -1; } int32_t VideoRenderAndroid::StartRender() { CriticalSectionScoped cs(&_critSect); if (_javaRenderThread) { // StartRender is called when this stream should start render. // However StopRender is not called when the streams stop rendering. // Thus the the thread is only deleted when the renderer is removed. WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s, Render thread already exist", __FUNCTION__); return 0; } _javaRenderThread.reset(new rtc::PlatformThread(JavaRenderThreadFun, this, "AndroidRenderThread")); _javaRenderThread->Start(); WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s: thread started", __FUNCTION__); _javaRenderThread->SetPriority(rtc::kRealtimePriority); return 0; } int32_t VideoRenderAndroid::StopRender() { WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:", __FUNCTION__); { CriticalSectionScoped cs(&_critSect); if (!_javaRenderThread) { return -1; } _javaShutDownFlag = true; _javaRenderEvent.Set(); } _javaShutdownEvent.Wait(3000); CriticalSectionScoped cs(&_critSect); _javaRenderThread->Stop(); _javaRenderThread.reset(); return 0; } void VideoRenderAndroid::ReDraw() { CriticalSectionScoped cs(&_critSect); // Allow redraw if it was more than 20ms since last. if (_lastJavaRenderEvent < TickTime::MillisecondTimestamp() - 20) { _lastJavaRenderEvent = TickTime::MillisecondTimestamp(); _javaRenderEvent.Set(); } } bool VideoRenderAndroid::JavaRenderThreadFun(void* obj) { return static_cast (obj)->JavaRenderThreadProcess(); } bool VideoRenderAndroid::JavaRenderThreadProcess() { _javaRenderEvent.Wait(1000); CriticalSectionScoped cs(&_critSect); if (!_javaRenderJniEnv) { // try to attach the thread and get the env // Attach this thread to JVM jint res = g_jvm->AttachCurrentThread(&_javaRenderJniEnv, NULL); // Get the JNI env for this thread if ((res < 0) || !_javaRenderJniEnv) { WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: Could not attach thread to JVM (%d, %p)", __FUNCTION__, res, _javaRenderJniEnv); return false; } } for (AndroidStreamMap::iterator it = _streamsMap.begin(); it != _streamsMap.end(); ++it) { it->second->DeliverFrame(_javaRenderJniEnv); } if (_javaShutDownFlag) { if (g_jvm->DetachCurrentThread() < 0) WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, "%s: Could not detach thread from JVM", __FUNCTION__); else { WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s: Java thread detached", __FUNCTION__); } _javaRenderJniEnv = NULL; _javaShutDownFlag = false; _javaShutdownEvent.Set(); return false; // Do not run this thread again. } return true; } VideoRenderType VideoRenderAndroid::RenderType() { return _renderType; } RawVideoType VideoRenderAndroid::PerferedVideoType() { return kVideoI420; } bool VideoRenderAndroid::FullScreen() { return false; } int32_t VideoRenderAndroid::GetGraphicsMemory( uint64_t& /*totalGraphicsMemory*/, uint64_t& /*availableGraphicsMemory*/) const { WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s - not supported on Android", __FUNCTION__); return -1; } int32_t VideoRenderAndroid::GetScreenResolution( uint32_t& /*screenWidth*/, uint32_t& /*screenHeight*/) const { WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s - not supported on Android", __FUNCTION__); return -1; } uint32_t VideoRenderAndroid::RenderFrameRate( const uint32_t /*streamId*/) { WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s - not supported on Android", __FUNCTION__); return -1; } int32_t VideoRenderAndroid::SetStreamCropping( const uint32_t /*streamId*/, const float /*left*/, const float /*top*/, const float /*right*/, const float /*bottom*/) { WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s - not supported on Android", __FUNCTION__); return -1; } int32_t VideoRenderAndroid::SetTransparentBackground(const bool enable) { WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s - not supported on Android", __FUNCTION__); return -1; } int32_t VideoRenderAndroid::ConfigureRenderer( const uint32_t streamId, const unsigned int zOrder, const float left, const float top, const float right, const float bottom) { WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s - not supported on Android", __FUNCTION__); return -1; } int32_t VideoRenderAndroid::SetText( const uint8_t textId, const uint8_t* text, const int32_t textLength, const uint32_t textColorRef, const uint32_t backgroundColorRef, const float left, const float top, const float rigth, const float bottom) { WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s - not supported on Android", __FUNCTION__); return -1; } int32_t VideoRenderAndroid::SetBitmap(const void* bitMap, const uint8_t pictureId, const void* colorKey, const float left, const float top, const float right, const float bottom) { WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s - not supported on Android", __FUNCTION__); return -1; } } // namespace webrtc