[M108 Migration][STT] Speech bring up. 45/286645/13
authorSuhaspoornachandra <s.poornachan@samsung.com>
Wed, 11 Jan 2023 04:53:28 +0000 (10:23 +0530)
committerBot Blink <blinkbot@samsung.com>
Wed, 25 Jan 2023 09:49:40 +0000 (09:49 +0000)
Refrence:
https://review.tizen.org/gerrit/c/274769/
https://review.tizen.org/gerrit/c/275686/

Change-Id: Ic439f156dc8804d7d2ad7eed59f3bd550153623a
Signed-off-by: Suhaspoornachandra <s.poornachan@samsung.com>
19 files changed:
content/browser/browser_interface_binders.cc
content/browser/browser_main_loop.cc
content/browser/browser_main_loop.h
content/browser/speech/speech_recognition_dispatcher_host.cc
content/browser/speech/speech_recognition_manager_impl.cc
content/browser/speech/speech_recognition_manager_impl.h
content/browser/speech/speech_recognizer.h
packaging/chromium-efl.spec
tizen_src/build/BUILD.gn
tizen_src/build/config/BUILD.gn
tizen_src/build/config/tizen_features.gni
tizen_src/build/gn_chromiumefl.sh
tizen_src/chromium_impl/content/browser/browser_efl.gni
tizen_src/chromium_impl/content/browser/speech/speech_recognizer_impl_tizen.cc [new file with mode: 0644]
tizen_src/chromium_impl/content/browser/speech/speech_recognizer_impl_tizen.h [new file with mode: 0644]
tizen_src/chromium_impl/content/browser/speech/tizen_speech_recognition_manager_delegate.cc [new file with mode: 0644]
tizen_src/chromium_impl/content/browser/speech/tizen_speech_recognition_manager_delegate.h [new file with mode: 0644]
tizen_src/ewk/efl_integration/content_browser_client_efl.cc
tizen_src/ewk/efl_integration/content_browser_client_efl.h

index adac0a4..541cf90 100644 (file)
@@ -737,10 +737,12 @@ void PopulateFrameBinders(RenderFrameHostImpl* host, mojo::BinderMap* map) {
   map->Add<blink::mojom::SharedWorkerConnector>(
       base::BindRepeating(&BindSharedWorkerConnector, base::Unretained(host)));
 
+#if defined(TIZEN_WEB_SPEECH_RECOGNITION)
   map->Add<blink::mojom::SpeechRecognizer>(
       base::BindRepeating(&SpeechRecognitionDispatcherHost::Create,
                           host->GetProcess()->GetID(), host->GetRoutingID()),
       GetIOThreadTaskRunner({}));
+#endif
 
   map->Add<blink::mojom::SpeechSynthesis>(base::BindRepeating(
       &RenderFrameHostImpl::GetSpeechSynthesis, base::Unretained(host)));
index eedf4ba..420eca0 100644 (file)
@@ -87,7 +87,6 @@
 #include "content/browser/screenlock_monitor/screenlock_monitor.h"
 #include "content/browser/screenlock_monitor/screenlock_monitor_device_source.h"
 #include "content/browser/sms/sms_provider.h"
-#include "content/browser/speech/speech_recognition_manager_impl.h"
 #include "content/browser/speech/tts_controller_impl.h"
 #include "content/browser/startup_data_impl.h"
 #include "content/browser/startup_task_runner.h"
 #include "ui/gfx/font_render_params.h"
 #include "ui/gfx/switches.h"
 
+#if defined(TIZEN_WEB_SPEECH_RECOGNITION)
+#include "content/browser/speech/speech_recognition_manager_impl.h"
+#endif
+
 #if defined(USE_AURA) || BUILDFLAG(IS_MAC)
 #include "content/browser/compositor/image_transport_factory.h"
 #endif
@@ -1111,14 +1114,14 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() {
     TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:MidiService");
     midi_service_->Shutdown();
   }
-
+#if defined(TIZEN_WEB_SPEECH_RECOGNITION)
   {
     TRACE_EVENT0("shutdown",
                  "BrowserMainLoop::Subsystem:SpeechRecognitionManager");
     io_thread_->task_runner()->DeleteSoon(
         FROM_HERE, speech_recognition_manager_.release());
   }
-
+#endif
   TtsControllerImpl::GetInstance()->Shutdown();
 
   memory_pressure_monitor_.reset();
@@ -1358,14 +1361,14 @@ void BrowserMainLoop::PostCreateThreadsImpl() {
     media_stream_manager_ =
         std::make_unique<MediaStreamManager>(audio_system_.get());
   }
-
+#if defined(TIZEN_WEB_SPEECH_RECOGNITION)
   {
     TRACE_EVENT0("startup",
                  "BrowserMainLoop::PostCreateThreads:InitSpeechRecognition");
     speech_recognition_manager_.reset(new SpeechRecognitionManagerImpl(
         audio_system_.get(), media_stream_manager_.get()));
   }
-
+#endif
   {
     TRACE_EVENT0("startup",
                  "BrowserMainLoop::PostCreateThreads::InitUserInputMonitor");
index 5e2ef10..acc602d 100644 (file)
@@ -93,11 +93,15 @@ class MediaStreamManager;
 class SaveFileManager;
 class ScreenlockMonitor;
 class SmsProvider;
-class SpeechRecognitionManagerImpl;
+
 class StartupTaskRunner;
 class TracingControllerImpl;
 struct MainFunctionParams;
 
+#if defined(TIZEN_WEB_SPEECH_RECOGNITION)
+class SpeechRecognitionManagerImpl;
+#endif
+
 namespace responsiveness {
 class Watcher;
 }  // namespace responsiveness
@@ -367,8 +371,10 @@ class CONTENT_EXPORT BrowserMainLoop {
 
   std::unique_ptr<midi::MidiService> midi_service_;
 
+#if defined(TIZEN_WEB_SPEECH_RECOGNITION)
   // Must be deleted on the IO thread.
   std::unique_ptr<SpeechRecognitionManagerImpl> speech_recognition_manager_;
+#endif
 
 #if BUILDFLAG(IS_WIN)
   std::unique_ptr<media::SystemMessageWindowWin> system_message_window_;
index bf5d858..f322ba6 100644 (file)
@@ -121,6 +121,15 @@ void SpeechRecognitionDispatcherHost::StartRequestOnUI(
     DCHECK_NE(embedder_render_frame_id, MSG_ROUTING_NONE);
   }
 
+#if BUILDFLAG(IS_TIZEN_TV) && defined(USE_WAYLAND)
+  if (SpeechRecognitionManagerImpl::GetInstance()) {
+    Evas_Object* parent_view =
+        static_cast<Evas_Object*>(web_contents->GetEflNativeView());
+    Evas* evas = evas_object_evas_get(parent_view);
+    SpeechRecognitionManagerImpl::GetInstance()->SetEvas(evas);
+  }
+#endif
+
   bool filter_profanities =
       SpeechRecognitionManagerImpl::GetInstance() &&
       SpeechRecognitionManagerImpl::GetInstance()->delegate() &&
index 0fdb61f..007f645 100644 (file)
 #include "content/browser/speech/speech_recognizer_impl_android.h"
 #endif
 
+#if defined(TIZEN_WEB_SPEECH_RECOGNITION) && BUILDFLAG(IS_EFL)
+#include "content/browser/speech/speech_recognizer_impl_tizen.h"
+#endif
+
 namespace content {
 
 SpeechRecognitionManager* SpeechRecognitionManager::manager_for_tests_;
@@ -180,7 +184,7 @@ int SpeechRecognitionManagerImpl::CreateSession(
   session->config = config;
   session->context = config.initial_context;
 
-#if !BUILDFLAG(IS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_EFL)
   // A SpeechRecognitionEngine (and corresponding Config) is required only
   // when using SpeechRecognizerImpl, which performs the audio capture and
   // endpointing in the browser. This is not the case of Android where, not
@@ -211,6 +215,15 @@ int SpeechRecognitionManagerImpl::CreateSession(
   session->recognizer = new SpeechRecognizerImpl(
       this, audio_system_, session_id, config.continuous,
       config.interim_results, google_remote_engine);
+#elif BUILDFLAG(IS_EFL)
+#if defined(TIZEN_WEB_SPEECH_RECOGNITION)
+  session->recognizer = new SpeechRecognizerImplTizen(this, session_id);
+#if BUILDFLAG(IS_TIZEN_TV) && defined(USE_WAYLAND)
+  session->recognizer->SetEvas(evas_);
+#endif
+#else
+  NOTREACHED();
+#endif
 #else
   session->recognizer = new SpeechRecognizerImplAndroid(this, session_id);
 #endif
index 9f26103..6f1b15e 100644 (file)
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-forward.h"
 #include "third_party/blink/public/mojom/speech/speech_recognition_error.mojom.h"
 
+#if BUILDFLAG(IS_TIZEN_TV) && defined(USE_WAYLAND)
+#include <Evas.h>
+#endif
+
 namespace media {
 class AudioSystem;
 }
@@ -89,6 +93,10 @@ class CONTENT_EXPORT SpeechRecognitionManagerImpl
 
   SpeechRecognitionManagerDelegate* delegate() const { return delegate_.get(); }
 
+#if BUILDFLAG(IS_TIZEN_TV) && defined(USE_WAYLAND)
+  void SetEvas(Evas* evas) { evas_ = evas; }
+#endif
+
  protected:
   // BrowserMainLoop is the only one allowed to instantiate this class.
   friend class BrowserMainLoop;
@@ -185,6 +193,10 @@ class CONTENT_EXPORT SpeechRecognitionManagerImpl
   std::unique_ptr<SpeechRecognitionManagerDelegate> delegate_;
   const int requester_id_;
 
+#if BUILDFLAG(IS_TIZEN_TV) && defined(USE_WAYLAND)
+  Evas* evas_ = nullptr;
+#endif
+
   // Used for posting asynchronous tasks (on the IO thread) without worrying
   // about this class being destroyed in the meanwhile (due to browser shutdown)
   // since tasks pending on a destroyed WeakPtr are automatically discarded.
index 681691d..86822c8 100644 (file)
 #include "base/memory/ref_counted.h"
 #include "content/common/content_export.h"
 
+#if BUILDFLAG(IS_TIZEN_TV) && defined(USE_WAYLAND)
+#include <Evas.h>
+#endif
+
 namespace content {
 
 class SpeechRecognitionEventListener;
@@ -32,6 +36,10 @@ class CONTENT_EXPORT SpeechRecognizer
   virtual bool IsActive() const = 0;
   virtual bool IsCapturingAudio() const = 0;
 
+#if BUILDFLAG(IS_TIZEN_TV) && defined(USE_WAYLAND)
+  virtual void SetEvas(Evas* evas) {}
+#endif
+
  protected:
   friend class base::RefCountedThreadSafe<SpeechRecognizer>;
 
index 01e43e7..baa95b3 100644 (file)
@@ -106,6 +106,7 @@ BuildRequires: pkgconfig(nss)
 BuildRequires: pkgconfig(scim)
 BuildRequires: pkgconfig(security-manager)
 BuildRequires: pkgconfig(sqlite3)
+BuildRequires: pkgconfig(stt)
 BuildRequires: pkgconfig(tts)
 BuildRequires: pkgconfig(ui-gadget-1)
 BuildRequires: pkgconfig(vconf)
@@ -141,6 +142,7 @@ BuildRequires: pkgconfig(xtst)
 %endif
 
 %if "%{?tizen_profile_name}" == "tv"
+BuildRequires: pkgconfig(capi-stt-wrapper-tv)
 BuildRequires: pkgconfig(vd-win-util)
 %endif
 
index 3fbf271..ce7e0ba 100644 (file)
@@ -579,6 +579,38 @@ config("tts-public") {
   }
 }
 
+config("capi-stt-wrapper-tv") {
+  if (tizen_product_tv) {
+    ldflags = [ "-lcapi-stt-wrapper-tv" ]
+  }
+}
+
+tizen_pkg_config("libcapi-stt-wrapper-tv") {
+  packages = []
+  if (tizen_product_tv) {
+    packages = [ "capi-stt-wrapper-tv" ]
+  }
+}
+
+config("stt") {
+  if (is_tizen) {
+    ldflags = [ "-lstt" ]
+  }
+}
+
+tizen_pkg_config("libstt") {
+  packages = []
+  if (is_tizen) {
+    packages = [ "stt" ]
+  }
+}
+
+config("stt-public") {
+  if (is_tizen) {
+    cflags = [ "-stt" ]
+  }
+}
+
 config("vconf") {
   if (is_tizen) {
     ldflags = [ "-lvconf" ]
index ed0f2b7..3b4e9c1 100644 (file)
@@ -39,6 +39,9 @@ config("tizen_feature_flags") {
         "TIZEN_MULTIMEDIA_USE_CAPI_AUDIO_IO",
       ]
     }
+    if (tizen_web_speech_recognition) {
+      defines += [ "TIZEN_WEB_SPEECH_RECOGNITION" ]
+    }
 
     # TODO: There are X11 dependencies in following condition.
     #       The files need to be implemented based on Wayland.
index 94b85b2..5e1ec9a 100644 (file)
@@ -46,6 +46,7 @@ declare_args() {
   tizen_multimedia_support = false
   tizen_multimedia = false
   tizen_tbm_support = false
+  tizen_web_speech_recognition = false
 }
 
 if (is_tizen && tizen_multimedia_support) {
index d2e392a..fd9662f 100755 (executable)
@@ -221,6 +221,7 @@ add_tizen_flags() {
   #            FIXME: http://165.213.149.170/jira/browse/TWF-610
   ADDITIONAL_GN_PARAMETERS+="tizen_multimedia=true
                              proprietary_codecs=true
+                             tizen_web_speech_recognition=true
                              tizen_tbm_support=false
                             "
 }
index b6a1022..2586fa5 100644 (file)
@@ -52,6 +52,19 @@ if (tizen_multimedia_support) {
   ]
 }
 
+if (tizen_web_speech_recognition) {
+  external_content_browser_efl_configs += [
+    "//tizen_src/build:stt",
+    "//tizen_src/build:libstt",
+  ]
+  if (tizen_product_tv) {
+    external_content_browser_efl_configs += [
+      "//tizen_src/build:capi-stt-wrapper-tv",
+      "//tizen_src/build:libcapi-stt-wrapper-tv",
+    ]
+  }
+}
+
 ##############################################################################
 # Dependency
 ##############################################################################
@@ -148,3 +161,12 @@ if (tizen_tbm_support) {
     "//tizen_src/chromium_impl/content/browser/media/browser_mediapacket_manager.h",
   ]
 }
+
+if (tizen_web_speech_recognition) {
+  external_content_browser_efl_sources += [
+    "//tizen_src/chromium_impl/content/browser/speech/speech_recognizer_impl_tizen.cc",
+    "//tizen_src/chromium_impl/content/browser/speech/speech_recognizer_impl_tizen.h",
+    "//tizen_src/chromium_impl/content/browser/speech/tizen_speech_recognition_manager_delegate.cc",
+    "//tizen_src/chromium_impl/content/browser/speech/tizen_speech_recognition_manager_delegate.h",
+  ]
+}
diff --git a/tizen_src/chromium_impl/content/browser/speech/speech_recognizer_impl_tizen.cc b/tizen_src/chromium_impl/content/browser/speech/speech_recognizer_impl_tizen.cc
new file mode 100644 (file)
index 0000000..72e2ac0
--- /dev/null
@@ -0,0 +1,533 @@
+// Copyright 2016 Samsung Electronics Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/speech/speech_recognizer_impl_tizen.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/speech_recognition_event_listener.h"
+#include "content/public/browser/speech_recognition_manager.h"
+#include "content/public/browser/speech_recognition_session_config.h"
+#include "third_party/blink/public/mojom/speech/speech_recognition_error.mojom.h"
+
+#define RUN_ON_BROWSER_IO_THREAD(METHOD, ...)                                 \
+  do {                                                                        \
+    if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {                     \
+      GetIOThreadTaskRunner({})->PostTask(                                    \
+          FROM_HERE, base::BindOnce(&SpeechRecognizerImplTizen::METHOD, this, \
+                                    ##__VA_ARGS__));                          \
+      return;                                                                 \
+    }                                                                         \
+  } while (0)
+
+#define RUN_ON_BROWSER_UI_THREAD(METHOD, ...)                                 \
+  do {                                                                        \
+    if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {                     \
+      GetUIThreadTaskRunner({})->PostTask(                                    \
+          FROM_HERE, base::BindOnce(&SpeechRecognizerImplTizen::METHOD, this, \
+                                    ##__VA_ARGS__));                          \
+      return;                                                                 \
+    }                                                                         \
+  } while (0)
+
+#define ENUM_CASE(x) \
+  case x:            \
+    return #x;       \
+    break
+
+#if !BUILDFLAG(IS_TIZEN_TV)
+#define stt_wrapper_get_error_message stt_get_error_message
+#define stt_wrapper_get_state stt_get_state
+#define stt_wrapper_destroy stt_destroy
+#define stt_wrapper_unset_error_cb stt_unset_error_cb
+#define stt_wrapper_unset_state_changed_cb stt_unset_state_changed_cb
+#define stt_wrapper_unset_recognition_result_cb stt_unset_recognition_result_cb
+#define stt_wrapper_unprepare stt_unprepare
+#define stt_wrapper_cancel stt_cancel
+#define stt_wrapper_start stt_start
+#define stt_wrapper_set_error_cb stt_set_error_cb
+#define stt_wrapper_set_state_changed_cb stt_set_state_changed_cb
+#define stt_wrapper_set_recognition_result_cb stt_set_recognition_result_cb
+#define stt_wrapper_create stt_create
+#define stt_wrapper_stop stt_stop
+#define stt_wrapper_prepare stt_prepare
+#define stt_wrapper_foreach_supported_languages stt_foreach_supported_languages
+#endif
+
+namespace {
+
+// Replace hyphen "-" in language with underscore "_". CAPI does not
+// support language string with hyphen. Example: en-US -> en_US.
+// change character case. Example: en_us -> en_US.
+std::string GetLanguageString(const std::string& language) {
+  std::string ret(language);
+  std::replace(ret.begin(), ret.end(), '-', '_');
+  std::transform(ret.begin(), ret.begin() + 2, ret.begin(), ::tolower);
+  std::transform(ret.end() - 2, ret.end(), ret.end() - 2, ::toupper);
+  return ret;
+}
+
+const char* GetErrorString(stt_error_e error) {
+  switch (error) {
+    ENUM_CASE(STT_ERROR_NONE);
+    ENUM_CASE(STT_ERROR_OUT_OF_MEMORY);
+    ENUM_CASE(STT_ERROR_IO_ERROR);
+    ENUM_CASE(STT_ERROR_INVALID_PARAMETER);
+    ENUM_CASE(STT_ERROR_TIMED_OUT);
+    ENUM_CASE(STT_ERROR_RECORDER_BUSY);
+    ENUM_CASE(STT_ERROR_OUT_OF_NETWORK);
+    ENUM_CASE(STT_ERROR_PERMISSION_DENIED);
+    ENUM_CASE(STT_ERROR_NOT_SUPPORTED);
+    ENUM_CASE(STT_ERROR_INVALID_STATE);
+    ENUM_CASE(STT_ERROR_INVALID_LANGUAGE);
+    ENUM_CASE(STT_ERROR_ENGINE_NOT_FOUND);
+    ENUM_CASE(STT_ERROR_OPERATION_FAILED);
+    ENUM_CASE(STT_ERROR_NOT_SUPPORTED_FEATURE);
+    ENUM_CASE(STT_ERROR_RECORDING_TIMED_OUT);
+    ENUM_CASE(STT_ERROR_NO_SPEECH);
+    ENUM_CASE(STT_ERROR_IN_PROGRESS_TO_READY);
+    ENUM_CASE(STT_ERROR_IN_PROGRESS_TO_RECORDING);
+    ENUM_CASE(STT_ERROR_IN_PROGRESS_TO_PROCESSING);
+    ENUM_CASE(STT_ERROR_SERVICE_RESET);
+  };
+
+  NOTREACHED() << "Invalid stt_error_e! (" << error << ")";
+  return "";
+}
+
+}  // namespace
+
+namespace content {
+
+blink::mojom::SpeechRecognitionErrorCode GetErrorCode(stt_error_e reason) {
+  switch (reason) {
+    case STT_ERROR_NONE:
+      return blink::mojom::SpeechRecognitionErrorCode::kNone;
+    case STT_ERROR_NO_SPEECH:
+      return blink::mojom::SpeechRecognitionErrorCode::kNoSpeech;
+    case STT_ERROR_RECORDING_TIMED_OUT:
+    case STT_ERROR_IO_ERROR:
+    case STT_ERROR_RECORDER_BUSY:
+      return blink::mojom::SpeechRecognitionErrorCode::kAudioCapture;
+    case STT_ERROR_OUT_OF_NETWORK:
+      return blink::mojom::SpeechRecognitionErrorCode::kNetwork;
+    case STT_ERROR_PERMISSION_DENIED:
+      return blink::mojom::SpeechRecognitionErrorCode::kNotAllowed;
+    case STT_ERROR_INVALID_LANGUAGE:
+      return blink::mojom::SpeechRecognitionErrorCode::kLanguageNotSupported;
+    case STT_ERROR_NOT_SUPPORTED:
+      return blink::mojom::SpeechRecognitionErrorCode::kServiceNotAllowed;
+    default:
+      return blink::mojom::SpeechRecognitionErrorCode::kAborted;
+  }
+}
+
+SpeechRecognizerImplTizen::SpeechRecognizerImplTizen(
+    SpeechRecognitionEventListener* listener,
+    int session_id)
+    : SpeechRecognizer(listener, session_id) {}
+
+SpeechRecognizerImplTizen::~SpeechRecognizerImplTizen() {
+  base::AutoLock auto_lock(abort_lock_);
+  if (!handle_ || is_aborted_)
+    return;
+
+  Destroy();
+}
+
+void SpeechRecognizerImplTizen::StartRecognition(const std::string& device_id) {
+  SpeechRecognitionSessionConfig config =
+      SpeechRecognitionManager::GetInstance()->GetSessionConfig(session_id());
+
+  recognition_type_ = config.interim_results ? STT_RECOGNITION_TYPE_FREE_PARTIAL
+                                             : STT_RECOGNITION_TYPE_FREE;
+  continuous_ = config.continuous;
+
+  if (config.language.empty()) {
+    LOG(ERROR) << "stt session #" << session_id()
+               << " language config is empty! Use 'en_US'";
+    is_lang_supported_ = true;
+    language_ = "en_US";
+  } else {
+    language_ = GetLanguageString(config.language);
+  }
+
+  // Initialize and prepare stt in UI thread
+  Prepare();
+}
+
+void SpeechRecognizerImplTizen::Prepare() {
+  RUN_ON_BROWSER_UI_THREAD(Prepare);
+
+  if (!Initialize())
+    return;
+
+  stt_wrapper_foreach_supported_languages(handle_, &OnSupportedLanguages, this);
+
+#if BUILDFLAG(IS_TIZEN_TV) && defined(USE_WAYLAND)
+  int err = stt_wrapper_prepare_with_evas(handle_, evas_);
+#else
+  int err = stt_wrapper_prepare(handle_);
+#endif
+  if (STT_ERROR_NONE != err)
+    HandleSttError(static_cast<stt_error_e>(err), FROM_HERE);
+}
+
+void SpeechRecognizerImplTizen::AbortRecognition() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  EndRecognition(FROM_HERE);
+}
+
+void SpeechRecognizerImplTizen::StopAudioCapture() {
+  RUN_ON_BROWSER_UI_THREAD(StopAudioCapture);
+  continuous_ = false;
+
+  if (GetCurrentState() != STT_STATE_RECORDING)
+    return;
+
+  int err = stt_wrapper_stop(handle_);
+  if (STT_ERROR_NONE != err)
+    HandleSttError(static_cast<stt_error_e>(err), FROM_HERE);
+}
+
+bool SpeechRecognizerImplTizen::IsActive() const {
+  return GetCurrentState() > STT_STATE_CREATED ? true : false;
+}
+
+bool SpeechRecognizerImplTizen::IsCapturingAudio() const {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  return GetCurrentState() >= STT_STATE_RECORDING ? true : false;
+}
+
+bool SpeechRecognizerImplTizen::Initialize() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  is_aborted_ = false;
+
+  int err = stt_wrapper_create(&handle_);
+  if (err != STT_ERROR_NONE) {
+    HandleSttError(static_cast<stt_error_e>(err), FROM_HERE);
+    return false;
+  }
+
+  err = stt_wrapper_set_recognition_result_cb(handle_, &OnRecognitionResult,
+                                              this);
+  if (err != STT_ERROR_NONE) {
+    HandleSttError(static_cast<stt_error_e>(err), FROM_HERE);
+    return false;
+  }
+
+  err = stt_wrapper_set_state_changed_cb(handle_, &OnStateChange, this);
+  if (err != STT_ERROR_NONE) {
+    HandleSttError(static_cast<stt_error_e>(err), FROM_HERE);
+    return false;
+  }
+
+  err = stt_wrapper_set_error_cb(handle_, &OnError, this);
+  if (err != STT_ERROR_NONE) {
+    HandleSttError(static_cast<stt_error_e>(err), FROM_HERE);
+    return false;
+  }
+  return true;
+}
+
+void SpeechRecognizerImplTizen::StartInternal() {
+  RUN_ON_BROWSER_UI_THREAD(StartInternal);
+
+  LOG(INFO) << "stt session #" << session_id()
+            << " language used : " << language_.c_str()
+            << " type : " << recognition_type_;
+  if (!is_lang_supported_) {
+    HandleSttError(STT_ERROR_INVALID_LANGUAGE, FROM_HERE);
+    return;
+  }
+
+  int err = stt_wrapper_start(handle_, language_.c_str(), recognition_type_);
+  if (STT_ERROR_NONE != err)
+    HandleSttError(static_cast<stt_error_e>(err), FROM_HERE);
+}
+
+void SpeechRecognizerImplTizen::Destroy() {
+  RUN_ON_BROWSER_UI_THREAD(Destroy);
+  if (!handle_)
+    return;
+
+  is_lang_supported_ = false;
+  continuous_ = false;
+  if (GetCurrentState() >= STT_STATE_RECORDING)
+    stt_wrapper_cancel(handle_);
+  if (GetCurrentState() == STT_STATE_READY) {
+#if BUILDFLAG(IS_TIZEN_TV) && defined(USE_WAYLAND)
+    stt_wrapper_unprepare_with_evas(handle_, evas_);
+#else
+    stt_wrapper_unprepare(handle_);
+#endif
+  }
+  if (GetCurrentState() == STT_STATE_CREATED) {
+    stt_wrapper_unset_recognition_result_cb(handle_);
+    stt_wrapper_unset_state_changed_cb(handle_);
+    stt_wrapper_unset_error_cb(handle_);
+  }
+
+  // Valid states to be called: created, Ready, Recording, Processing
+#if BUILDFLAG(IS_TIZEN_TV) && defined(USE_WAYLAND)
+  int err = stt_wrapper_destroy_with_evas(handle_, evas_);
+#else
+  int err = stt_wrapper_destroy(handle_);
+#endif
+  if (err != STT_ERROR_NONE)
+    LOG(ERROR) << "stt session #" << session_id() << " stt_destroy failed with "
+               << GetErrorString(static_cast<stt_error_e>(err));
+  else
+    handle_ = nullptr;
+}
+
+bool SpeechRecognizerImplTizen::ShouldStartRecognition() const {
+  return GetCurrentState() >= STT_STATE_READY;
+}
+
+// There is no default stt state. So use |STT_STATE_CREATED|
+// This will work as we will use it to determine audio capturing.
+stt_state_e SpeechRecognizerImplTizen::GetCurrentState() const {
+  if (!handle_ || is_aborted_)
+    return STT_STATE_CREATED;
+
+  stt_state_e state = STT_STATE_CREATED;
+  int err = stt_wrapper_get_state(handle_, &state);
+  if (err != STT_ERROR_NONE)
+    LOG(ERROR) << "stt session #" << session_id() << " stt_get_state FAILED!";
+  return state;
+}
+
+void SpeechRecognizerImplTizen::RecognitionResults(
+    bool no_speech,
+    std::vector<blink::mojom::SpeechRecognitionResultPtr>& results) {
+  {
+    base::AutoLock auto_lock(abort_lock_);
+    if (is_aborted_) {
+      LOG(INFO) << "stt engine is in error state";
+      return;
+    }
+  }
+
+  if (no_speech && !continuous_) {
+    HandleSttError(STT_ERROR_NO_SPEECH, FROM_HERE);
+    return;
+  }
+
+  listener()->OnRecognitionResults(session_id(), results);
+}
+
+void SpeechRecognizerImplTizen::HandleSttError(stt_error_e error,
+                                               const base::Location& location) {
+  RUN_ON_BROWSER_IO_THREAD(HandleSttError, error, location);
+  LOG(ERROR) << "stt session #" << session_id() << " error "
+             << GetErrorString(error) << " from " << location.function_name()
+             << "(" << location.line_number() << ")";
+  {
+    base::AutoLock auto_lock(abort_lock_);
+    if (!handle_ || is_aborted_)
+      return;
+  }
+
+  blink::mojom::SpeechRecognitionError recognition_error;
+  recognition_error.code = GetErrorCode(static_cast<stt_error_e>(error));
+  recognition_error.details = blink::mojom::SpeechAudioErrorDetails::kNone;
+
+  listener()->OnRecognitionError(session_id(), recognition_error);
+  EndRecognition(FROM_HERE);
+}
+
+void SpeechRecognizerImplTizen::HandleSttResultError(const std::string error) {
+  RUN_ON_BROWSER_IO_THREAD(HandleSttResultError, error);
+  LOG(ERROR) << "stt session #" << session_id() << " no result as " << error;
+
+  {
+    base::AutoLock auto_lock(abort_lock_);
+    if (!handle_ || is_aborted_)
+      return;
+  }
+
+  if (error == STT_RESULT_MESSAGE_ERROR_TOO_SOON ||
+      error == STT_RESULT_MESSAGE_ERROR_TOO_SHORT ||
+      error == STT_RESULT_MESSAGE_ERROR_TOO_LONG ||
+      error == STT_RESULT_MESSAGE_ERROR_TOO_QUIET ||
+      error == STT_RESULT_MESSAGE_ERROR_TOO_LOUD ||
+      error == STT_RESULT_MESSAGE_ERROR_TOO_FAST) {
+    blink::mojom::SpeechRecognitionError recognition_error;
+    recognition_error.code = blink::mojom::SpeechRecognitionErrorCode::kNoMatch;
+    recognition_error.details = blink::mojom::SpeechAudioErrorDetails::kNone;
+    listener()->OnRecognitionError(session_id(), recognition_error);
+  }
+}
+
+void SpeechRecognizerImplTizen::RecognitionStarted() {
+  RUN_ON_BROWSER_IO_THREAD(RecognitionStarted);
+
+  listener()->OnAudioStart(session_id());
+  listener()->OnSoundStart(session_id());
+  listener()->OnRecognitionStart(session_id());
+}
+
+void SpeechRecognizerImplTizen::AudioCaptureEnd() {
+  RUN_ON_BROWSER_IO_THREAD(AudioCaptureEnd);
+
+  listener()->OnSoundEnd(session_id());
+  listener()->OnAudioEnd(session_id());
+}
+
+void SpeechRecognizerImplTizen::EndRecognition(const base::Location& location) {
+  RUN_ON_BROWSER_IO_THREAD(EndRecognition, location);
+
+  {
+    base::AutoLock auto_lock(abort_lock_);
+    if (is_aborted_)
+      return;
+
+    is_aborted_ = true;
+  }
+
+  LOG(INFO) << "stt session #" << session_id()
+            << " ending recognition called from " << location.function_name()
+            << "(" << location.line_number() << ")";
+
+  if (GetCurrentState() == STT_STATE_RECORDING)
+    AudioCaptureEnd();
+
+  listener()->OnRecognitionEnd(session_id());
+  Destroy();
+}
+
+bool SpeechRecognizerImplTizen::OnSupportedLanguages(stt_h stt,
+                                                     const char* language,
+                                                     void* user_data) {
+  SpeechRecognizerImplTizen* speech =
+      static_cast<SpeechRecognizerImplTizen*>(user_data);
+  if (!speech)
+    return false;
+
+  if (!speech->language_.compare(language)) {
+    speech->is_lang_supported_ = true;
+    return false;
+  }
+
+  return true;
+}
+
+void SpeechRecognizerImplTizen::OnRecognitionResult(stt_h stt,
+                                                    stt_result_event_e event,
+                                                    const char** data,
+                                                    int data_count,
+                                                    const char* msg,
+                                                    void* user_data) {
+  SpeechRecognizerImplTizen* speech =
+      static_cast<SpeechRecognizerImplTizen*>(user_data);
+  if (!speech)
+    return;
+
+  if (event == STT_RESULT_EVENT_ERROR) {
+    speech->HandleSttResultError(msg);
+    return;
+  }
+
+  bool no_speech = true;
+  blink::mojom::SpeechRecognitionResultPtr result =
+      blink::mojom::SpeechRecognitionResult::New();
+  for (int i = 0; i < data_count; i++) {
+    if (data[i] && data[i][0] != ' ') {
+      no_speech = false;
+      std::u16string utterance;
+      base::UTF8ToUTF16(data[i], strlen(data[i]), &utterance);
+      result->hypotheses.push_back(
+          blink::mojom::SpeechRecognitionHypothesis::New(utterance, 1.0));
+    }
+  }
+
+  result->is_provisional =
+      (event == STT_RESULT_EVENT_FINAL_RESULT) ? false : true;
+
+  std::vector<blink::mojom::SpeechRecognitionResultPtr> results;
+  results.push_back(std::move(result));
+
+  speech->RecognitionResults(no_speech, results);
+}
+
+void SpeechRecognizerImplTizen::OnStateChange(stt_h stt,
+                                              stt_state_e previous,
+                                              stt_state_e current,
+                                              void* user_data) {
+  SpeechRecognizerImplTizen* speech =
+      static_cast<SpeechRecognizerImplTizen*>(user_data);
+  if (!speech)
+    return;
+
+  LOG(INFO) << "stt session #" << speech->session_id()
+            << " state change called. Previous : " << previous
+            << " Current : " << current;
+
+  switch (current) {
+    case STT_STATE_READY:
+      switch (previous) {
+        case STT_STATE_CREATED:
+          speech->StartInternal();
+          break;
+        case STT_STATE_RECORDING:
+          if (!speech->is_aborted_)
+            speech->HandleSttError(STT_ERROR_NO_SPEECH, FROM_HERE);
+          break;
+        case STT_STATE_PROCESSING:
+          // If continuous attribute is true recognition shouldn't stop after
+          // receiving one final result. So start the recognition again.
+          if (speech->continuous_)
+            speech->StartInternal();
+          // |stt_recognition_result_cb| is called even when a partial result is
+          // obtained. So, need to end recognition only when the state change
+          // is confirmed.
+          else
+            speech->EndRecognition(FROM_HERE);
+          break;
+        default:
+          break;
+      }
+      break;
+    case STT_STATE_RECORDING:
+      speech->RecognitionStarted();
+      break;
+    case STT_STATE_PROCESSING:
+      speech->AudioCaptureEnd();
+      break;
+    default:
+      break;
+  }
+}
+
+void SpeechRecognizerImplTizen::OnError(stt_h stt,
+                                        stt_error_e reason,
+                                        void* user_data) {
+  SpeechRecognizerImplTizen* speech =
+      static_cast<SpeechRecognizerImplTizen*>(user_data);
+  if (!speech)
+    return;
+#if BUILDFLAG(IS_TIZEN_TV)
+  char* error_msg = nullptr;
+  const std::string no_speech_msg = "voice_engine.error.no_speech";
+  int ret = stt_wrapper_get_error_message(stt, &error_msg);
+  if (ret == STT_ERROR_NONE) {
+    LOG(INFO) << "stt session #" << speech->session_id()
+              << "err msg : " << error_msg;
+    if (!no_speech_msg.compare(error_msg))
+      reason = STT_ERROR_NO_SPEECH;
+    if (error_msg)
+      free(error_msg);
+  } else {
+    LOG(ERROR) << "stt session #" << speech->session_id()
+               << " stt_wrapper_get_error_message:"
+               << GetErrorString(static_cast<stt_error_e>(ret));
+  }
+#endif
+  speech->HandleSttError(reason, FROM_HERE);
+}
+
+}  // namespace content
diff --git a/tizen_src/chromium_impl/content/browser/speech/speech_recognizer_impl_tizen.h b/tizen_src/chromium_impl/content/browser/speech/speech_recognizer_impl_tizen.h
new file mode 100644 (file)
index 0000000..1cb47ce
--- /dev/null
@@ -0,0 +1,93 @@
+// Copyright 2016 Samsung Electronics Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_SPEECH_SPEECH_RECOGNIZER_IMPL_TIZEN_
+#define CONTENT_BROWSER_SPEECH_SPEECH_RECOGNIZER_IMPL_TIZEN_
+
+#include <vector>
+
+#include "base/location.h"
+#include "content/browser/speech/speech_recognizer.h"
+#include "third_party/blink/public/mojom/speech/speech_recognition_result.mojom.h"
+
+#if BUILDFLAG(IS_TIZEN_TV)
+#include <stt-wrapper.h>
+#if defined(USE_WAYLAND)
+#include <Evas.h>
+#endif
+#else
+#include <stt.h>
+#endif
+
+namespace content {
+
+class CONTENT_EXPORT SpeechRecognizerImplTizen : public SpeechRecognizer {
+ public:
+  SpeechRecognizerImplTizen(SpeechRecognitionEventListener* listener,
+                            int session_id);
+
+  // SpeechRecognizer methods.
+  void StartRecognition(const std::string& device_id) override;
+  void AbortRecognition() override;
+  void StopAudioCapture() override;
+  bool IsActive() const override;
+  bool IsCapturingAudio() const override;
+#if BUILDFLAG(IS_TIZEN_TV) && defined(USE_WAYLAND)
+  void SetEvas(Evas* evas) override { evas_ = evas; }
+#endif
+
+ private:
+  SpeechRecognizerImplTizen(const SpeechRecognizerImplTizen&) = delete;
+  SpeechRecognizerImplTizen& operator=(const SpeechRecognizerImplTizen&) =
+      delete;
+
+  ~SpeechRecognizerImplTizen() override;
+
+  void Prepare();
+  bool Initialize();
+  void StartInternal();
+  void Destroy();
+  stt_state_e GetCurrentState() const;
+  bool ShouldStartRecognition() const;
+  void RecognitionResults(
+      bool no_speech,
+      std::vector<blink::mojom::SpeechRecognitionResultPtr>& results);
+  void HandleSttError(stt_error_e error, const base::Location& location);
+  void HandleSttResultError(const std::string error);
+  void RecognitionStarted();
+  void AudioCaptureEnd();
+  void EndRecognition(const base::Location& location);
+
+  // Callbacks
+  static bool OnSupportedLanguages(stt_h stt,
+                                   const char* language,
+                                   void* user_data);
+  static void OnRecognitionResult(stt_h stt,
+                                  stt_result_event_e event,
+                                  const char** data,
+                                  int data_count,
+                                  const char* msg,
+                                  void* user_data);
+  static void OnStateChange(stt_h stt,
+                            stt_state_e previous,
+                            stt_state_e current,
+                            void* user_data);
+  static void OnError(stt_h stt, stt_error_e reason, void* user_data);
+
+#if BUILDFLAG(IS_TIZEN_TV) && defined(USE_WAYLAND)
+  Evas* evas_ = nullptr;
+#endif
+
+  stt_h handle_ = nullptr;
+  std::string language_;
+  bool is_lang_supported_ = false;
+  bool continuous_ = false;
+  const char* recognition_type_ = "";
+  bool is_aborted_ = false;
+  base::Lock abort_lock_;
+};
+
+}  // namespace content
+
+#endif  // BROWSER_SPEECH_SPEECH_RECOGNIZER_IMPL_TIZEN_
diff --git a/tizen_src/chromium_impl/content/browser/speech/tizen_speech_recognition_manager_delegate.cc b/tizen_src/chromium_impl/content/browser/speech/tizen_speech_recognition_manager_delegate.cc
new file mode 100644 (file)
index 0000000..8435e66
--- /dev/null
@@ -0,0 +1,37 @@
+// Copyright 2016 Samsung Electronics Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/speech/tizen_speech_recognition_manager_delegate.h"
+
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+
+#if BUILDFLAG(IS_TIZEN_TV)
+#include "tizen_src/ewk/efl_integration/common/application_type.h"
+#endif
+
+namespace content {
+
+void TizenSpeechRecognitionManagerDelegate::CheckRecognitionIsAllowed(
+    int session_id,
+    base::OnceCallback<void(bool ask_user, bool is_allowed)> callback) {
+  // For tizen, we expect speech recognition to happen when requested.
+
+  // Therefore we simply authorize it by calling back with is_allowed=true. The
+  // first parameter, ask_user, is set to false because we don't want to prompt
+  // the user for permission with an infobar.
+
+  // In browser, a pop-up for permission to use microphone will show up.
+  // In web app, however, the user agrees on the access to microphone
+  // when the app is installed on device. So the pop-up will not show up.
+#if BUILDFLAG(IS_TIZEN_TV) && defined(WRT_BRINGUP)
+  GetIOThreadTaskRunner({})->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), IsTIZENWRT(), false));
+#else
+  GetIOThreadTaskRunner({})->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), false, true));
+#endif
+}
+
+}  // namespace content
diff --git a/tizen_src/chromium_impl/content/browser/speech/tizen_speech_recognition_manager_delegate.h b/tizen_src/chromium_impl/content/browser/speech/tizen_speech_recognition_manager_delegate.h
new file mode 100644 (file)
index 0000000..6ac79bb
--- /dev/null
@@ -0,0 +1,41 @@
+// Copyright 2016 Samsung Electronics Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_SPEECH_TIZEN_SPEECH_RECOGNITION_MANAGER_DELEGATE_H_
+#define CONTENT_BROWSER_SPEECH_TIZEN_SPEECH_RECOGNITION_MANAGER_DELEGATE_H_
+
+#include "base/bind.h"
+#include "content/public/browser/speech_recognition_manager_delegate.h"
+
+namespace content {
+
+class SpeechRecognitionEventListener;
+// This delegate is used by the speech recognition manager to
+// check for permission to record audio. For tizen, we always authorize
+// speech recognition.
+class TizenSpeechRecognitionManagerDelegate
+    : public SpeechRecognitionManagerDelegate {
+ public:
+  TizenSpeechRecognitionManagerDelegate() {}
+  ~TizenSpeechRecognitionManagerDelegate() override {}
+
+ private:
+  TizenSpeechRecognitionManagerDelegate(
+      const TizenSpeechRecognitionManagerDelegate&) = delete;
+  TizenSpeechRecognitionManagerDelegate& operator=(
+      const TizenSpeechRecognitionManagerDelegate&) = delete;
+
+  void CheckRecognitionIsAllowed(
+      int session_id,
+      base::OnceCallback<void(bool ask_user, bool is_allowed)> callback)
+      override;
+  SpeechRecognitionEventListener* GetEventListener() override {
+    return nullptr;
+  }
+  bool FilterProfanities(int render_process_id) override { return false; }
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_SPEECH_TIZEN_SPEECH_RECOGNITION_MANAGER_DELEGATE_H_
index 7b36d0f..23882ee 100644 (file)
 
 #include "private/ewk_notification_private.h"
 
+#if defined(TIZEN_WEB_SPEECH_RECOGNITION)
+#include "content/browser/speech/tizen_speech_recognition_manager_delegate.h"
+#endif
+
 using web_contents_utils::WebContentsFromFrameID;
 using web_contents_utils::WebContentsFromViewID;
 using web_contents_utils::WebViewFromWebContents;
@@ -250,6 +254,13 @@ bool ContentBrowserClientEfl::HasErrorPage(int http_status_code) {
       error_page::Error::kHttpErrorDomain, http_status_code);
 }
 
+#if defined(TIZEN_WEB_SPEECH_RECOGNITION)
+SpeechRecognitionManagerDelegate*
+ContentBrowserClientEfl::CreateSpeechRecognitionManagerDelegate() {
+  return new TizenSpeechRecognitionManagerDelegate();
+}
+#endif
+
 void ContentBrowserClientEfl::AllowCertificateError(
     WebContents* web_contents,
     int cert_error,
index cef8cb6..7b3f8f1 100644 (file)
@@ -21,6 +21,11 @@ namespace content {
 class BrowserMainPartsEfl;
 class NotificationControllerEfl;
 class SharedURLLoaderFactoryEfl;
+
+#if defined(TIZEN_WEB_SPEECH_RECOGNITION)
+class SpeechRecognitionManagerDelegate;
+#endif
+
 class WebContents;
 class WebContentsView;
 
@@ -59,6 +64,11 @@ class ContentBrowserClientEfl : public ContentBrowserClient {
                        bool opener_suppressed,
                        bool* no_javascript_access) override;
 
+#if defined(TIZEN_WEB_SPEECH_RECOGNITION)
+  SpeechRecognitionManagerDelegate* CreateSpeechRecognitionManagerDelegate()
+      override;
+#endif
+
   scoped_refptr<QuotaPermissionContext> CreateQuotaPermissionContext() override;
 
   bool HasErrorPage(int http_status_code) override;