Add features to download images over http protocol. 00/34900/5
authorYoonsang Lee <ysang114.lee@samsung.com>
Wed, 4 Feb 2015 08:43:40 +0000 (17:43 +0900)
committerYoonsang Lee <ysang114.lee@samsung.com>
Thu, 12 Feb 2015 23:40:53 +0000 (08:40 +0900)
- Add ResourceThreadImage::Download() to download an image using libcurl.
- Add ResoureThreadBase::RequestDownload, ResourceThreadBase::Download()
- Refactor ResoureThreadImage::Load(), Decode().
- Add one more thread object, mThreadImageRemote, to ResourceBitmapRequester to download images over http protocol in a seperate thread.
  Without this seperate thread, local image files which can be loaded very shortly may be delayed until other time-consuming remote images loading is finished.
- Employ libcurl to download files over http protocol.

Change-Id: Ie10e5b0b1fa374af74a48ad2c9f0df301d27fe5e

13 files changed:
build/tizen/adaptor/Makefile.am
build/tizen/configure.ac
packaging/dali-adaptor-mobile.spec
packaging/dali-adaptor-tv.spec
packaging/dali-adaptor-wearable.spec
packaging/dali-adaptor.spec
platform-abstractions/slp/resource-loader/resource-bitmap-requester.cpp
platform-abstractions/slp/resource-loader/resource-bitmap-requester.h
platform-abstractions/slp/resource-loader/resource-thread-base.cpp
platform-abstractions/slp/resource-loader/resource-thread-base.h
platform-abstractions/slp/resource-loader/resource-thread-distance-field.h
platform-abstractions/slp/resource-loader/resource-thread-image.cpp
platform-abstractions/slp/resource-loader/resource-thread-image.h

index 7b1bbc1..bce2054 100644 (file)
@@ -235,6 +235,7 @@ libdali_adaptor_la_CXXFLAGS = \
                       $(SENSOR_CFLAGS) \
                       $(LIBDRM_CFLAGS) \
                       $(LIBEXIF_CFLAGS) \
+                      $(LIBCURL_CFLAGS) \
                       $(ASSIMP_CFLAGS) \
                       $(CAPI_SYSTEM_SYSTEM_SETTINGS_CFLAGS)
 
@@ -254,6 +255,7 @@ libdali_adaptor_la_LIBADD = \
                       $(SENSOR_LIBS) \
                       $(LIBDRM_LIBS) \
                       $(LIBEXIF_LIBS) \
+                      $(LIBCURL_LIBS) \
                       $(CAPI_SYSTEM_SYSTEM_SETTINGS_LIBS) \
                       $(CAPI_APPFW_APPLICATION_LIBS) \
                       -lgif \
index 3b0590f..6aec94b 100644 (file)
@@ -44,6 +44,7 @@ PKG_CHECK_MODULES(PNG, libpng)
 PKG_CHECK_MODULES(XML, libxml-2.0)
 PKG_CHECK_MODULES(LIBEXIF, libexif)
 PKG_CHECK_MODULES(LIBDRM, libdrm)
+PKG_CHECK_MODULES(LIBCURL, libcurl)
 
 # Check for availability of BulletPhysics
 PKG_CHECK_EXISTS(bullet, [
index aeb9277..59b533d 100644 (file)
@@ -44,6 +44,7 @@ BuildRequires:  pkgconfig(capi-system-system-settings)
 BuildRequires:  pkgconfig(libpng)
 BuildRequires:  pkgconfig(opengl-es-20)
 BuildRequires:  pkgconfig(efl-assist)
+BuildRequires:  libcurl-devel
 
 %if 0%{?dali_assimp_plugin}
 BuildRequires:  pkgconfig(assimp)
index 3269568..5138b01 100644 (file)
@@ -41,6 +41,7 @@ BuildRequires:  pkgconfig(xfixes)
 BuildRequires:  pkgconfig(xdamage)
 BuildRequires:  pkgconfig(utilX)
 BuildRequires:  pkgconfig(gles20)
+BuildRequires:  libcurl-devel
 
 %if 0%{?dali_assimp_plugin}
 BuildRequires:  pkgconfig(assimp)
index a56f880..8ed97ec 100644 (file)
@@ -43,6 +43,7 @@ BuildRequires:  pkgconfig(capi-system-system-settings)
 BuildRequires:  pkgconfig(libpng)
 BuildRequires:  pkgconfig(gles20)
 BuildRequires:  pkgconfig(efl-assist)
+BuildRequires:  libcurl-devel
 
 %if 0%{?dali_assimp_plugin}
 BuildRequires:  pkgconfig(assimp)
index 0ae856c..05d1773 100644 (file)
@@ -65,6 +65,7 @@ BuildRequires:  pkgconfig(capi-system-system-settings)
 BuildRequires:  pkgconfig(libpng)
 BuildRequires:  pkgconfig(glesv2)
 BuildRequires:  pkgconfig(egl)
+BuildRequires:  libcurl-devel
 
 %if %{with wayland}
 BuildRequires:  pkgconfig(ecore-wayland)
index fd08ba2..e9523fe 100644 (file)
@@ -28,25 +28,29 @@ namespace SlpPlatform
 ResourceBitmapRequester::ResourceBitmapRequester( ResourceLoader& resourceLoader )
 : ResourceRequesterBase( resourceLoader )
 {
-  mThreadImage  = new ResourceThreadImage( resourceLoader );
+  mThreadImageLocal  = new ResourceThreadImage( resourceLoader, false );
+  mThreadImageRemote  = new ResourceThreadImage( resourceLoader, true );
   mThreadDistanceField = new ResourceThreadDistanceField( resourceLoader );
 }
 
 ResourceBitmapRequester::~ResourceBitmapRequester()
 {
-  delete mThreadImage;
+  delete mThreadImageLocal;
+  delete mThreadImageRemote;
   delete mThreadDistanceField;
 }
 
 void ResourceBitmapRequester::Pause()
 {
-  mThreadImage->Pause();
+  mThreadImageLocal->Pause();
+  mThreadImageRemote->Pause();
   mThreadDistanceField->Pause();
 }
 
 void ResourceBitmapRequester::Resume()
 {
-  mThreadImage->Resume();
+  mThreadImageLocal->Resume();
+  mThreadImageRemote->Resume();
   mThreadDistanceField->Resume();
 }
 
@@ -56,13 +60,38 @@ void ResourceBitmapRequester::LoadResource( Integration::ResourceRequest& reques
   BitmapResourceType* resType = static_cast<BitmapResourceType*>(request.GetType());
   if( resType )
   {
-    // Work out if the resource is in memory or a file:
-    const ResourceThreadBase::RequestType requestType = request.GetResource().Get() ? ResourceThreadBase::RequestDecode : ResourceThreadBase::RequestLoad;
-
     // Work out what thread to decode / load the image on:
-    ResourceThreadBase* const imageThread = mThreadImage;
+    ResourceThreadBase* const localImageThread = mThreadImageLocal;
+    ResourceThreadBase* const remoteImageThread = mThreadImageRemote;
     ResourceThreadBase* const distanceFieldThread = mThreadDistanceField ;
-    ResourceThreadBase* const workerThread = ( !resType->imageAttributes.IsDistanceField() ) ? imageThread : distanceFieldThread;
+    ResourceThreadBase* workerThread;
+
+    // Work out if the resource is in memory, a file, or in a remote server:
+    ResourceThreadBase::RequestType requestType;
+    if( request.GetResource().Get() )
+    {
+      requestType = ResourceThreadBase::RequestDecode;
+      workerThread = localImageThread;
+    }
+    else
+    {
+      const std::string& resourcePath = request.GetPath();
+      if( resourcePath.length() > 7 && strncasecmp( resourcePath.c_str(), "http://", 7 ) == 0 )
+      {
+        requestType = ResourceThreadBase::RequestDownload;
+        workerThread = remoteImageThread;
+      }
+      else
+      {
+        requestType = ResourceThreadBase::RequestLoad;
+        workerThread = localImageThread;
+      }
+    }
+
+    if( resType->imageAttributes.IsDistanceField() )
+    {
+      workerThread = distanceFieldThread;
+    }
 
     // Dispatch the job to the right thread:
     workerThread->AddRequest( request, requestType );
@@ -82,7 +111,8 @@ void ResourceBitmapRequester::SaveResource(const Integration::ResourceRequest& r
 
 void ResourceBitmapRequester::CancelLoad(Integration::ResourceId id, Integration::ResourceTypeId typeId)
 {
-  mThreadImage->CancelRequest(id);
+  mThreadImageLocal->CancelRequest(id);
+  mThreadImageRemote->CancelRequest(id);
   mThreadDistanceField->CancelRequest(id);
 }
 
index b012504..a1158f9 100644 (file)
@@ -75,7 +75,8 @@ public:
   virtual void CancelLoad(Integration::ResourceId id, Integration::ResourceTypeId typeId);
 
 protected:
-  ResourceThreadImage*          mThreadImage;           ///< Image loader thread object
+  ResourceThreadImage*          mThreadImageLocal;      ///< Image loader thread object to load images in local machine
+  ResourceThreadImage*          mThreadImageRemote;     ///< Image loader thread object to download images in remote http server
   ResourceThreadDistanceField*  mThreadDistanceField;   ///< Distance field generator thread.
 };
 
index 1708b24..7a8e4e4 100644 (file)
@@ -272,6 +272,12 @@ void ResourceThreadBase::ProcessNextRequest()
       }
       break;
 
+      case RequestDownload:
+      {
+        Download(*request);
+      }
+      break;
+
       case RequestDecode:
       {
         Decode(*request);
@@ -299,6 +305,13 @@ void ResourceThreadBase::UninstallLogging()
   Dali::Integration::Log::UninstallLogFunction();
 }
 
+void ResourceThreadBase::Download(const Integration::ResourceRequest& request)
+{
+  DALI_LOG_TRACE_METHOD(mLogFilter);
+  DALI_LOG_WARNING("Resource Downloading from a remote server not supported for this type.");
+  ///! If you need this for a subclassed thread, look to ResourceThreadImage::Download() for an example implementation.
+}
+
 void ResourceThreadBase::Decode(const Integration::ResourceRequest& request)
 {
   DALI_LOG_TRACE_METHOD(mLogFilter);
index 4dab9d7..ae5aadd 100644 (file)
@@ -48,6 +48,8 @@ public:
   {
     /** Pull a resource out of the platform's file system. */
     RequestLoad,
+    /** Pull a resource over http protocol. */
+    RequestDownload,
     /** Pull a resource out of a memory buffer. */
     RequestDecode,
     /** Push a resource's data out to the file system. */
@@ -127,6 +129,12 @@ protected:
   virtual void Load(const Integration::ResourceRequest& request) = 0;
 
   /**
+   * Download a resource
+   * @param[in] request  The requested resource/file url and attributes
+   */
+  virtual void Download(const Integration::ResourceRequest& request);
+
+  /**
    * Decode a resource exactly as if it were being loaded but source its data
    * from a memory buffer attached directly to the request object.
    * @param[in] request  The requested resource data and attributes
index 8884c57..82a34a7 100644 (file)
@@ -61,7 +61,7 @@ private:
    */
   virtual void Save(const Integration::ResourceRequest& request);
 
-}; // class ResourceThreadImage
+}; // class ResourceThreadDistanceField
 
 } // namespace SlpPlatform
 
index 76ce3a0..a339807 100755 (executable)
@@ -21,6 +21,7 @@
 #include <dali/integration-api/debug.h>
 #include <dali/integration-api/resource-cache.h>
 #include <dali/integration-api/resource-types.h>
+#include <curl/curl.h>
 #include "portable/file-closer.h"
 #include "image-loaders/image-loader.h"
 
@@ -32,7 +33,7 @@ namespace Dali
 namespace SlpPlatform
 {
 
-ResourceThreadImage::ResourceThreadImage(ResourceLoader& resourceLoader)
+ResourceThreadImage::ResourceThreadImage(ResourceLoader& resourceLoader, bool forRemoteImage)
 : ResourceThreadBase(resourceLoader)
 {
 }
@@ -46,6 +47,142 @@ void ResourceThreadImage::Load(const ResourceRequest& request)
   DALI_LOG_TRACE_METHOD( mLogFilter );
   DALI_LOG_INFO( mLogFilter, Debug::Verbose, "%s(%s)\n", __FUNCTION__, request.GetPath().c_str() );
 
+  LoadImageFromLocalFile(request);
+}
+
+void ResourceThreadImage::Download(const ResourceRequest& request)
+{
+  bool succeeded;
+
+  DALI_LOG_TRACE_METHOD( mLogFilter );
+  DALI_LOG_INFO( mLogFilter, Debug::Verbose, "%s(%s)\n", __FUNCTION__, request.GetPath().c_str() );
+
+  Dali::Vector<uint8_t> dataBuffer;
+  size_t dataSize;
+  succeeded = DownloadRemoteImageIntoMemory( request, dataBuffer, dataSize );
+  if( succeeded )
+  {
+    DecodeImageFromMemory(static_cast<void*>(&dataBuffer[0]), dataBuffer.Size(), request);
+  }
+}
+
+void ResourceThreadImage::Decode(const ResourceRequest& request)
+{
+  DALI_LOG_TRACE_METHOD( mLogFilter );
+  DALI_LOG_INFO(mLogFilter, Debug::Verbose, "%s(%s)\n", __FUNCTION__, request.GetPath().c_str());
+
+  // Get the blob of binary data that we need to decode:
+  DALI_ASSERT_DEBUG( request.GetResource() );
+
+  DALI_ASSERT_DEBUG( 0 != dynamic_cast<Dali::RefCountedVector<uint8_t>*>( request.GetResource().Get() ) && "Only blobs of binary data can be decoded." );
+  Dali::RefCountedVector<uint8_t>* const encodedBlob = reinterpret_cast<Dali::RefCountedVector<uint8_t>*>( request.GetResource().Get() );
+
+  if( 0 != encodedBlob )
+  {
+    const size_t blobSize     = encodedBlob->GetVector().Size();
+    uint8_t * const blobBytes = &(encodedBlob->GetVector()[0]);
+    DecodeImageFromMemory(blobBytes, blobSize, request);
+  }
+  else
+  {
+    FailedResource resource(request.GetId(), FailureUnknown);
+    mResourceLoader.AddFailedLoad(resource);
+  }
+}
+
+void ResourceThreadImage::Save(const Integration::ResourceRequest& request)
+{
+  DALI_LOG_TRACE_METHOD( mLogFilter );
+  DALI_ASSERT_DEBUG( request.GetType()->id == ResourceBitmap );
+  DALI_LOG_WARNING( "Image saving not supported on background resource threads." );
+}
+
+bool ResourceThreadImage::DownloadRemoteImageIntoMemory(const Integration::ResourceRequest& request, Dali::Vector<uint8_t>& dataBuffer, size_t& dataSize)
+{
+  bool succeeded = true;
+  CURLcode cresult;
+
+  CURL* curl_handle = curl_easy_init();
+  curl_easy_setopt( curl_handle, CURLOPT_VERBOSE, 0 );
+  curl_easy_setopt( curl_handle, CURLOPT_URL, request.GetPath().c_str() );
+  curl_easy_setopt( curl_handle, CURLOPT_FAILONERROR, 1 );
+
+  // Download header first to get data size
+  char* headerBytes = NULL;
+  size_t headerSize = 0;
+  FILE* header_fp = open_memstream( &headerBytes, &headerSize );
+  double size;
+
+  if( NULL != header_fp)
+  {
+    curl_easy_setopt( curl_handle, CURLOPT_HEADER, 1 );
+    curl_easy_setopt( curl_handle, CURLOPT_NOBODY, 1 );
+    curl_easy_setopt( curl_handle, CURLOPT_WRITEDATA, header_fp );
+
+    cresult = curl_easy_perform( curl_handle );
+    if( cresult == CURLE_OK )
+    {
+      curl_easy_getinfo( curl_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &size );
+    }
+    else
+    {
+      DALI_LOG_WARNING( "Failed to download file to load \"%s\"\n", request.GetPath().c_str() );
+      succeeded = false;
+    }
+
+    fclose( header_fp );
+  }
+  else
+  {
+    succeeded = false;
+  }
+
+  if( NULL != headerBytes )
+  {
+    free( headerBytes );
+  }
+
+  if( succeeded )
+  {
+    // Download file data
+    dataSize = static_cast<size_t>( size );
+    dataBuffer.Reserve( dataSize );
+    dataBuffer.Resize( dataSize );
+
+    Dali::Internal::Platform::FileCloser fileCloser( static_cast<void*>(&dataBuffer[0]), dataSize, "wb" );
+    FILE* data_fp = fileCloser.GetFile();
+    if( NULL != data_fp )
+    {
+      curl_easy_setopt( curl_handle, CURLOPT_HEADER, 0 );
+      curl_easy_setopt( curl_handle, CURLOPT_NOBODY, 0 );
+      curl_easy_setopt( curl_handle, CURLOPT_WRITEDATA, data_fp );
+
+      cresult = curl_easy_perform( curl_handle );
+      if( CURLE_OK != cresult )
+      {
+        DALI_LOG_WARNING( "Failed to download file to load \"%s\"\n", request.GetPath().c_str() );
+        succeeded = false;
+      }
+    }
+    else
+    {
+      succeeded = false;
+    }
+  }
+
+  curl_easy_cleanup( curl_handle );
+
+  if( !succeeded )
+  {
+    FailedResource resource(request.GetId(), FailureUnknown);
+    mResourceLoader.AddFailedLoad(resource);
+  }
+
+  return succeeded;
+}
+
+void ResourceThreadImage::LoadImageFromLocalFile(const Integration::ResourceRequest& request)
+{
   bool fileNotFound = false;
   BitmapPtr bitmap = 0;
   bool result = false;
@@ -53,7 +190,7 @@ void ResourceThreadImage::Load(const ResourceRequest& request)
   Dali::Internal::Platform::FileCloser fileCloser( request.GetPath().c_str(), "rb" );
   FILE * const fp = fileCloser.GetFile();
 
-  if( fp != NULL )
+  if( NULL != fp )
   {
     result = ImageLoader::ConvertStreamToBitmap( *request.GetType(), request.GetPath(), fp, *this, bitmap );
     // Last chance to interrupt a cancelled load before it is reported back to clients
@@ -92,46 +229,31 @@ void ResourceThreadImage::Load(const ResourceRequest& request)
   }
 }
 
-void ResourceThreadImage::Decode(const ResourceRequest& request)
+void ResourceThreadImage::DecodeImageFromMemory(void* blobBytes, size_t blobSize, const Integration::ResourceRequest& request)
 {
-  DALI_LOG_TRACE_METHOD( mLogFilter );
-  DALI_LOG_INFO(mLogFilter, Debug::Verbose, "%s(%s)\n", __FUNCTION__, request.GetPath().c_str());
-
   BitmapPtr bitmap = 0;
 
-  // Get the blob of binary data that we need to decode:
-  DALI_ASSERT_DEBUG( request.GetResource() );
-
-  DALI_ASSERT_DEBUG( 0 != dynamic_cast<Dali::RefCountedVector<uint8_t>*>( request.GetResource().Get() ) && "Only blobs of binary data can be decoded." );
-  Dali::RefCountedVector<uint8_t>* const encodedBlob = reinterpret_cast<Dali::RefCountedVector<uint8_t>*>( request.GetResource().Get() );
+  DALI_ASSERT_DEBUG( blobSize > 0U );
+  DALI_ASSERT_DEBUG( blobBytes != 0U );
 
-  if( encodedBlob != 0 )
+  if( blobBytes != 0 && blobSize > 0U )
   {
-    const size_t blobSize     = encodedBlob->GetVector().Size();
-    uint8_t * const blobBytes = &(encodedBlob->GetVector()[0]);
-    DALI_ASSERT_DEBUG( blobSize > 0U );
-    DALI_ASSERT_DEBUG( blobBytes != 0U );
-
-    if( blobBytes != 0 && blobSize > 0U )
+    // Open a file handle on the memory buffer:
+    Dali::Internal::Platform::FileCloser fileCloser( blobBytes, blobSize, "rb" );
+    FILE * const fp = fileCloser.GetFile();
+    if ( NULL != fp )
     {
-      // Open a file handle on the memory buffer:
-      Dali::Internal::Platform::FileCloser fileCloser( blobBytes, blobSize, "rb" );
-      FILE * const fp = fileCloser.GetFile();
-      if ( fp != NULL )
+      bool result = ImageLoader::ConvertStreamToBitmap( *request.GetType(), request.GetPath(), fp, StubbedResourceLoadingClient(), bitmap );
+      if ( result && bitmap )
+      {
+        // Construct LoadedResource and ResourcePointer for image data
+        LoadedResource resource( request.GetId(), request.GetType()->id, ResourcePointer( bitmap.Get() ) );
+        // Queue the loaded resource
+        mResourceLoader.AddLoadedResource( resource );
+      }
+      else
       {
-        bool result = ImageLoader::ConvertStreamToBitmap( *request.GetType(), request.GetPath(), fp, StubbedResourceLoadingClient(), bitmap );
-
-        if ( result && bitmap )
-        {
-          // Construct LoadedResource and ResourcePointer for image data
-          LoadedResource resource( request.GetId(), request.GetType()->id, ResourcePointer( bitmap.Get() ) );
-          // Queue the loaded resource
-          mResourceLoader.AddLoadedResource( resource );
-        }
-        else
-        {
-          DALI_LOG_WARNING( "Unable to decode bitmap supplied as in-memory blob.\n" );
-        }
+        DALI_LOG_WARNING( "Unable to decode bitmap supplied as in-memory blob.\n" );
       }
     }
   }
@@ -143,14 +265,6 @@ void ResourceThreadImage::Decode(const ResourceRequest& request)
   }
 }
 
-void ResourceThreadImage::Save(const Integration::ResourceRequest& request)
-{
-  DALI_LOG_TRACE_METHOD( mLogFilter );
-  DALI_ASSERT_DEBUG( request.GetType()->id == ResourceBitmap );
-  DALI_LOG_WARNING( "Image saving not supported on background resource threads." );
-}
-
-
 } // namespace SlpPlatform
 
 } // namespace Dali
index 36be426..9b5079b 100644 (file)
@@ -35,7 +35,7 @@ public:
    * Constructor
    * @param[in] resourceLoader A reference to the ResourceLoader
    */
-  ResourceThreadImage(ResourceLoader& resourceLoader);
+  ResourceThreadImage(ResourceLoader& resourceLoader, bool forRemoteImage);
 
   /**
    * Destructor
@@ -50,6 +50,11 @@ private:
   virtual void Load(const Integration::ResourceRequest& request);
 
   /**
+   * @copydoc ResourceThreadBase::Download
+   */
+  virtual void Download(const Integration::ResourceRequest& request);
+
+  /**
    * @copydoc ResourceThreadBase::Decode
    */
   virtual void Decode(const Integration::ResourceRequest& request);
@@ -59,6 +64,27 @@ private:
    */
   virtual void Save(const Integration::ResourceRequest& request);
 
+  /**
+   * Download a requested image into a memory buffer.
+   * @param[in] request  The requested resource/file url and attributes
+   * @param[out] dataBuffer  A memory buffer object to be written with downloaded image data.
+   * @param[out] dataSize  The size of the memory buffer.
+   */
+  bool DownloadRemoteImageIntoMemory(const Integration::ResourceRequest& request, Dali::Vector<uint8_t>& dataBuffer, size_t& dataSize);
+
+  /**
+   * Load a requested image from a local file.
+   * @param[in] request  The requested resource/file url and attributes
+   */
+  void LoadImageFromLocalFile(const Integration::ResourceRequest& request);
+
+  /**
+   * Decode a requested image from a memory buffer.
+   * @param[in] blobBytes  A pointer to the memory buffer containig the requested image data.
+   * @param[in] blobSize  The size of the memory buffer containing the requested image data.
+   * @param[in] request  The requested resource/file url and attributes
+   */
+  void DecodeImageFromMemory(void* blobBytes, size_t blobSize, const Integration::ResourceRequest& request);
 }; // class ResourceThreadImage
 
 } // namespace SlpPlatform