diff --git a/media/jni/Android.mk b/media/jni/Android.mk index 2a89a2a9dfe5..2ddb2870a259 100644 --- a/media/jni/Android.mk +++ b/media/jni/Android.mk @@ -27,9 +27,11 @@ LOCAL_SHARED_LIBRARIES := \ libcamera_client \ libsqlite \ libmtp \ - libusbhost + libusbhost \ + libexif LOCAL_C_INCLUDES += \ + external/jhead \ external/tremor/Tremor \ frameworks/base/core/jni \ frameworks/base/media/libmedia \ diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp index e71ed4cbd22d..b78af44db361 100644 --- a/media/jni/android_mtp_MtpDatabase.cpp +++ b/media/jni/android_mtp_MtpDatabase.cpp @@ -35,6 +35,10 @@ #include "MtpUtils.h" #include "mtp.h" +extern "C" { +#include "jhead.h" +} + using namespace android; // ---------------------------------------------------------------------------- @@ -141,6 +145,8 @@ class MyMtpDatabase : public MtpDatabase { virtual MtpResponseCode getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info); + virtual void* getThumbnail(MtpObjectHandle handle, size_t& outThumbSize); + virtual MtpResponseCode getObjectFilePath(MtpObjectHandle handle, MtpString& outFilePath, int64_t& outFileLength, @@ -778,10 +784,67 @@ MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle, info.mName = strdup((const char *)temp); env->ReleaseCharArrayElements(mStringBuffer, str, 0); + // read EXIF data for thumbnail information + if (info.mFormat == MTP_FORMAT_EXIF_JPEG || info.mFormat == MTP_FORMAT_JFIF) { + MtpString path; + int64_t length; + MtpObjectFormat format; + if (getObjectFilePath(handle, path, length, format) == MTP_RESPONSE_OK) { + ResetJpgfile(); + // Start with an empty image information structure. + memset(&ImageInfo, 0, sizeof(ImageInfo)); + ImageInfo.FlashUsed = -1; + ImageInfo.MeteringMode = -1; + ImageInfo.Whitebalance = -1; + strncpy(ImageInfo.FileName, (const char *)path, PATH_MAX); + if (ReadJpegFile((const char*)path, READ_METADATA)) { + Section_t* section = FindSection(M_EXIF); + if (section) { + info.mThumbCompressedSize = ImageInfo.ThumbnailSize; + info.mThumbFormat = MTP_FORMAT_EXIF_JPEG; + info.mImagePixWidth = ImageInfo.Width; + info.mImagePixHeight = ImageInfo.Height; + } + } + DiscardData(); + } + } + checkAndClearExceptionFromCallback(env, __FUNCTION__); return MTP_RESPONSE_OK; } +void* MyMtpDatabase::getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) { + MtpString path; + int64_t length; + MtpObjectFormat format; + void* result = NULL; + outThumbSize = 0; + + if (getObjectFilePath(handle, path, length, format) == MTP_RESPONSE_OK + && (format == MTP_FORMAT_EXIF_JPEG || format == MTP_FORMAT_JFIF)) { + ResetJpgfile(); + // Start with an empty image information structure. + memset(&ImageInfo, 0, sizeof(ImageInfo)); + ImageInfo.FlashUsed = -1; + ImageInfo.MeteringMode = -1; + ImageInfo.Whitebalance = -1; + strncpy(ImageInfo.FileName, (const char *)path, PATH_MAX); + if (ReadJpegFile((const char*)path, READ_METADATA)) { + Section_t* section = FindSection(M_EXIF); + if (section) { + outThumbSize = ImageInfo.ThumbnailSize; + result = malloc(outThumbSize); + if (result) + memcpy(result, section->Data + ImageInfo.ThumbnailOffset + 8, outThumbSize); + } + DiscardData(); + } + } + + return result; +} + MtpResponseCode MyMtpDatabase::getObjectFilePath(MtpObjectHandle handle, MtpString& outFilePath, int64_t& outFileLength, diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp index 0b0c80d11d8c..817eac055f72 100644 --- a/media/mtp/MtpDataPacket.cpp +++ b/media/mtp/MtpDataPacket.cpp @@ -388,6 +388,16 @@ int MtpDataPacket::writeDataHeader(int fd, uint32_t length) { int ret = ::write(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE); return (ret < 0 ? ret : 0); } + +int MtpDataPacket::writeData(int fd, void* data, uint32_t length) { + MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, length + MTP_CONTAINER_HEADER_SIZE); + MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA); + int ret = ::write(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE); + if (ret == MTP_CONTAINER_HEADER_SIZE) + ret = ::write(fd, data, length); + return (ret < 0 ? ret : 0); +} + #endif // MTP_DEVICE #ifdef MTP_HOST diff --git a/media/mtp/MtpDataPacket.h b/media/mtp/MtpDataPacket.h index 577cea152f69..8a08948b43e8 100644 --- a/media/mtp/MtpDataPacket.h +++ b/media/mtp/MtpDataPacket.h @@ -100,6 +100,7 @@ class MtpDataPacket : public MtpPacket { // write our data to the given file descriptor int write(int fd); int writeDataHeader(int fd, uint32_t length); + int writeData(int fd, void* data, uint32_t length); #endif #ifdef MTP_HOST diff --git a/media/mtp/MtpDatabase.h b/media/mtp/MtpDatabase.h index d7bde00a4dc7..4e6ac7a8a3d8 100644 --- a/media/mtp/MtpDatabase.h +++ b/media/mtp/MtpDatabase.h @@ -84,6 +84,8 @@ class MtpDatabase { virtual MtpResponseCode getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info) = 0; + virtual void* getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) = 0; + virtual MtpResponseCode getObjectFilePath(MtpObjectHandle handle, MtpString& outFilePath, int64_t& outFileLength, diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp index b744b5b4c84a..4a8fd3e33d91 100644 --- a/media/mtp/MtpServer.cpp +++ b/media/mtp/MtpServer.cpp @@ -50,7 +50,7 @@ static const MtpOperationCode kSupportedOperationCodes[] = { MTP_OPERATION_GET_OBJECT_HANDLES, MTP_OPERATION_GET_OBJECT_INFO, MTP_OPERATION_GET_OBJECT, -// MTP_OPERATION_GET_THUMB, + MTP_OPERATION_GET_THUMB, MTP_OPERATION_DELETE_OBJECT, MTP_OPERATION_SEND_OBJECT_INFO, MTP_OPERATION_SEND_OBJECT, @@ -370,6 +370,9 @@ bool MtpServer::handleRequest() { case MTP_OPERATION_GET_OBJECT: response = doGetObject(); break; + case MTP_OPERATION_GET_THUMB: + response = doGetThumb(); + break; case MTP_OPERATION_GET_PARTIAL_OBJECT: case MTP_OPERATION_GET_PARTIAL_OBJECT_64: response = doGetPartialObject(operation); @@ -736,6 +739,22 @@ MtpResponseCode MtpServer::doGetObject() { return MTP_RESPONSE_OK; } +MtpResponseCode MtpServer::doGetThumb() { + MtpObjectHandle handle = mRequest.getParameter(1); + size_t thumbSize; + void* thumb = mDatabase->getThumbnail(handle, thumbSize); + if (thumb) { + // send data + mData.setOperationCode(mRequest.getOperationCode()); + mData.setTransactionID(mRequest.getTransactionID()); + mData.writeData(mFD, thumb, thumbSize); + free(thumb); + return MTP_RESPONSE_OK; + } else { + return MTP_RESPONSE_GENERAL_ERROR; + } +} + MtpResponseCode MtpServer::doGetPartialObject(MtpOperationCode operation) { if (!hasStorage()) return MTP_RESPONSE_INVALID_OBJECT_HANDLE; diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h index b06eb287ba58..859a18e63b66 100644 --- a/media/mtp/MtpServer.h +++ b/media/mtp/MtpServer.h @@ -133,6 +133,7 @@ class MtpServer { MtpResponseCode doGetObjectPropList(); MtpResponseCode doGetObjectInfo(); MtpResponseCode doGetObject(); + MtpResponseCode doGetThumb(); MtpResponseCode doGetPartialObject(MtpOperationCode operation); MtpResponseCode doSendObjectInfo(); MtpResponseCode doSendObject();