当前位置: 首页 > 工具软件 > Focal Camera > 使用案例 >

MTK平台Camera图片的Exif信息

茹正初
2023-12-01

我们都知道,MTK平台Camera在拍照时生成的照片中包含相应照片的Exif信息,而这些信息是从什么地方来的呢?今天我们就来捋一下Code思路。

(1)生成照片通过JpegNode

这里以v2.0的JpegNode.cpp举例

//vendor/mediatek/proprietary/hardware/mtkcam3/pipeline/hwnode/JpegNode/v2.0/JpegNode.cpp

static void onProcessRequest(
    JpegNodeImp& jpg,
    Request& req
)
{

	//(1)prepare
    CAM_TRACE_NAME(__FUNCTION__);
    sp<encode_frame> enc_frame = req.getEncodeFrame();
    // lock buffer
    if (req.lockMeta() != OK) {
        MY_LOGE("lock request meta failed!");
        req.mbIsEncodeJpegError = MTRUE;
        return;
    }
    if (req.prepareMeta() != OK) {
        MY_LOGE("prepareMeta failed!");
        req.mbIsEncodeJpegError = MTRUE;
        return;
    }

    if (req.lockInImg() != OK) {
        MY_LOGE("lock request input image failed");
        req.mbIsEncodeJpegError = MTRUE;
        return;
    }

    if (req.prepareExif() != OK) {
        MY_LOGE("prepareExif failed!");
        return;
    }

    if (req.lockOutImg() != OK) {
        MY_LOGE("lock request output image failed");
        req.mbIsEncodeJpegError = MTRUE;
        return;
    }

    enc_frame->muJpegSwMode = jpg.mPropJpegSwMode;

    // (2)encode flow
    if (!jpg.mbAppnOnly || jpg.mIsIspHidlHeif) {
        std::thread encodeThumbThread;
        if (enc_frame->mbHasThumbnail) {
            encodeThumbThread = std::thread(&JpegNodeImp::encodeThumbnailJpeg, &jpg, std::ref(enc_frame));
        }
        jpg.encodeMainJpeg(enc_frame);

        if(enc_frame->mbHasThumbnail &&
                encodeThumbThread.joinable()) {
            encodeThumbThread.join();
        }
    } else {
        if (enc_frame->mbHasThumbnail)
            jpg.encodeThumbnailJpeg(enc_frame);
    }

    req.updateMetadata(enc_frame->mParams);

    if (req.unlock() != OK) {
        MY_LOGE("request unlock failed!");
    }

    // (3)encode exif
    jpg.encodeExif(enc_frame);

    //...
    return;
}

我们这里主要关注exif信息是如何产生的?所以跟踪第三步encode exif。

MVOID
JpegNodeImp::
encodeExif(
    sp<encode_frame>& pEncodeFrame)
{
   
	//...
	
    MUINT const usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN;
    MBOOL ret = pOutImageBuffer->lockBuf(getNodeName(), usage);
    MY_LOGA_IF(!ret, "%s", "lockBuffer failed, please check VA usage!");

    size_t exifSize  = 0;
    MINT8 * pExifBuf = reinterpret_cast<MINT8*>(pOutImageBuffer->getBufVA(0));

	//这里主要关注是如何make Exif信息
    if( pExifBuf == NULL
            || OK != ExifUtils::makeExifHeader(pEncodeFrame, pExifBuf, exifSize, mbAppnOnly)
      ) {
        pEncodeFrame->mbSuccess = MFALSE;
        MY_LOGE("frame %u make exif header failed: buf %p, size %zu",
                pEncodeFrame->mpFrame->getFrameNo(),
                pExifBuf, exifSize);
    }
    if( pExifBuf )
        pExifBuf = NULL;

    size_t JpegBitStreamSize = 0;
    if (pEncodeFrame->mpJpeg_Main != NULL) {
        JpegBitStreamSize = pEncodeFrame->exif.getHeaderSize() +
            pEncodeFrame->mpJpeg_Main->getBitstreamSize();
        pEncodeFrame->mpJpeg_Main->setBitstreamSize(
                JpegBitStreamSize);
    } else {
        JpegBitStreamSize = pEncodeFrame->exif.getHeaderSize();
    }

	//...
}

我们进入makeExifHeader函数。

MERROR
ExifUtils::
makeExifHeader(
    sp<encode_frame> rpEncodeFrame,
    MINT8 * const pOutExif,
    size_t& rOutExifSize, // [IN/OUT] in: exif buf size, out: exif header size
    MBOOL const isAppnOnly
)
{
    MERROR ret;
    MBOOL needSOI = MTRUE;
    
    ret = rpEncodeFrame->exif.make((MUINTPTR)pOutExif, rOutExifSize, needSOI);
    rpEncodeFrame->exif.uninit();
    
    return ret;
}

这里的exif类型是StdExif,所以我们可以定位到StdExif文件。

(2)进入StdExif文件

我们搜索一下Code,发现StdExif有好几处,如下:

//vendor/mediatek/proprietary/hardware/mtkcam/legacy/exif/camera/StdExif.cpp
//vendor/mediatek/proprietary/hardware/mtkcam/legacy/exif/v3/StdExif.cpp
//vendor/mediatek/proprietary/hardware/mtkcam/utils/exif/v3/StdExif.cpp
//vendor/mediatek/proprietary/hardware/mtkcam-core/utils/exif/v3/StdExif.cpp

那我们如何确定是走的哪个文件中的呢?可通过如下Log来判断:

05-31 23:43:27.593   973 19585 I mtkcam/exif: [make] out buffer(0x7252bc3000)
05-31 23:43:27.593   973 19585 I mtkcam/exif: [updateStdExif] strDateTime(2022:05:31 23:43:27), strSubSecTime(579)
05-31 23:43:27.593   973 19585 I mtkcam/exif: [updateStdExif] property: make(UTIME), model(TM152)
05-31 23:43:27.594   973 19585 E mtkcam/exif: [make] not support ICC profile 0 (make){#642:vendor/mediatek/proprietary/hardware/mtkcam/utils/exif/v3/StdExif.cpp}

一旦拍照之后就会出现make exif信息的Log,可通过如上Log来看能否辨别相应的文件。

我们接下来进入StdExif.cpp文件的make函数。

//vendor/mediatek/proprietary/hardware/mtkcam/utils/exif/v3/StdExif.cpp

status_t
StdExif::
make(
    MUINTPTR const  outputExifBuf,
    size_t& rOutputExifSize,
    MBOOL enableSOI
)
{
    int ret = 0;
    mpOutputExifBuf = outputExifBuf;
    // set 0 first for error return
    rOutputExifSize = 0;
    MY_LOGI("out buffer(%#" PRIxPTR ")", getBufAddr());

    unsigned int u4OutputExifSize = 0;
    
    //这里主要有如下两个exif信息相关的结构体
    exifAPP1Info_s exifApp1Info;
    exifImageInfo_s exifImgInfo;

    //  (1) Fill exifApp1Info
    updateStdExif(&exifApp1Info);

    //  (2) Fill exifImgInfo
    ::memset(&exifImgInfo, 0, sizeof(exifImageInfo_t));
    exifImgInfo.bufAddr     = getBufAddr();
    exifImgInfo.mainWidth   = mExifParam.u4ImageWidth;
    exifImgInfo.mainHeight  = mExifParam.u4ImageHeight;
    exifImgInfo.thumbSize   = getThumbnailSize();

    ret = mpBaseExif->exifApp1Make(&exifImgInfo, &exifApp1Info, &u4OutputExifSize, enableSOI);
    rOutputExifSize = (size_t)u4OutputExifSize;

    //  (4) Append App2
    int app2 = 2;
    unsigned int app2ReturnSize = 0;
    int size = mICCSize; // Data(n bytes)
    unsigned char *pAddr = (unsigned char*)getBufAddr()+ getStdExifSize() + getThumbnailSize();
    MY_LOGD_IF(mLogLevel,"offset %zu buf %p ", getStdExifSize() + getThumbnailSize() , pAddr);
    if(mICCIdx == EXIF_ICC_PROFILE_SRGB) {
        ret = mpBaseExif->exifAppnMake(app2, pAddr, (unsigned char*)&icc_profile_srgb, size, &app2ReturnSize, 0);
    }
    else if(mICCIdx == EXIF_ICC_PROFILE_DCI_P3) {
        ret = mpBaseExif->exifAppnMake(app2, pAddr, (unsigned char*)&icc_profile_display_p3, size, &app2ReturnSize, 0);
    }
    else
        MY_LOGE("not support ICC profile %d", mICCIdx);
    // return app2ReturnSize is mICCSize + 2 +2 (Data(n bytes) + Data size(2 bytes) + Data tag(2 bytes))
    // (3) Append debug exif
    if ( isEnableDbgExif() )
    {
        updateDbgExif();
    }

    return (status_t)ret;
}

相关exif信息的部分结构体如下:

//vendor/mediatek/proprietary/hardware/mtkcam/utils/exif/common/IBaseExif.h

typedef struct exifImageInfo_s {
    MUINTPTR bufAddr;
    unsigned int mainWidth;
    unsigned int mainHeight;
    unsigned int thumbSize;
} exifImageInfo_t;

typedef struct exifAPP1Info_s {
    unsigned int imgIndex;      // mtk definition: the index of continuous shot image.
    unsigned int groupID;       // mtk definition: group ID for continuous shot.
    unsigned int bestFocusH;    // mtk definition: focus value (H) for best shot.
    unsigned int bestFocusL;    // mtk definition: focus value (L) for best shot.
    unsigned int refocusPos;            // mtk definition: for image refocus. JPEG(main sensor) in left or right position.
    unsigned char strJpsFileName[32];   // mtk definition: for image refocus. JPS file name for calculating depth map.
    unsigned int exposureTime[2];
    unsigned int fnumber[2];
    unsigned int shutterSpeed[2];
    int brightness[2];
    int exposureBiasValue[2];
    unsigned int focalLength[2];
    unsigned int maxApertureValue[2];
    unsigned short focalLength35mm;
    unsigned short orientation;
    unsigned short exposureProgram;
    unsigned short isoSpeedRatings;
    unsigned short isoType;
    unsigned int isoSpeed;
    unsigned short meteringMode;
    unsigned short flash;
    unsigned short whiteBalanceMode;
    unsigned short reserved;
    unsigned char strImageDescription[32];
    unsigned char strMake[32];
    unsigned char strModel[32];
    unsigned char strSoftware[32];
    unsigned char strDateTime[20];
    unsigned char strSubSecTime[4];
    unsigned char gpsLatitudeRef[2];
    unsigned char gpsLongitudeRef[2];
    unsigned char reserved02;
    unsigned int digitalZoomRatio[2];
    unsigned short sceneCaptureType;
    unsigned short lightSource;
    unsigned char strFlashPixVer[8];
    unsigned short exposureMode;
    unsigned short reserved03;
    int gpsIsOn;
    int gpsAltitude[2];
    int gpsLatitude[8];
    int gpsLongitude[8];
    int gpsTimeStamp[8];
    unsigned char gpsDateStamp[12];
    unsigned char gpsProcessingMethod[64];
} exifAPP1Info_t;

这里我们主要看updateStdExif(&exifApp1Info)函数,看主要有那些exif信息被产生。

void
StdExif::
updateStdExif(exifAPP1Info_s* exifApp1Info)
{
    ::memset(exifApp1Info, 0, sizeof(exifAPP1Info_s));

    /*********************************************************************************
                                           GPS
    **********************************************************************************/
    if  (mExifParam.u4GpsIsOn == 1) {
        float latitude = atof((char*)mExifParam.uGPSLatitude);
        float longitude = atof((char*)mExifParam.uGPSLongitude);
        long long timestamp = atol((char*)mExifParam.uGPSTimeStamp);
        char const*pgpsProcessingMethod = (char*)mExifParam.uGPSProcessingMethod;
        //
        // Set GPS Info
        if (latitude >= 0) {
            strncpy((char *)exifApp1Info->gpsLatitudeRef, "N", sizeof(exifApp1Info->gpsLatitudeRef)/sizeof(char));
        }
        else {
            strncpy((char *)exifApp1Info->gpsLatitudeRef, "S", sizeof(exifApp1Info->gpsLatitudeRef)/sizeof(char));
            latitude *= -1;     // make it positive
        }
        if (longitude >= 0) {
            strncpy((char *)exifApp1Info->gpsLongitudeRef, "E", sizeof(exifApp1Info->gpsLongitudeRef)/sizeof(char));
        }
        else {
            strncpy((char *)exifApp1Info->gpsLongitudeRef, "W", sizeof(exifApp1Info->gpsLongitudeRef)/sizeof(char));
            longitude *= -1;    // make it positive
        }
        exifApp1Info->gpsIsOn = 1;
        // Altitude
        exifApp1Info->gpsAltitude[0] = mExifParam.u4GPSAltitude;
        exifApp1Info->gpsAltitude[1] = 1;
        // Latitude
        exifApp1Info->gpsLatitude[0] = (int) latitude;
        exifApp1Info->gpsLatitude[1] = 1;
        latitude -= exifApp1Info->gpsLatitude[0];
        latitude *= 60;
        exifApp1Info->gpsLatitude[2] = (int) latitude;
        exifApp1Info->gpsLatitude[3] = 1;
        latitude -= exifApp1Info->gpsLatitude[2];
        latitude *= 60;
        latitude *= 10000;
        exifApp1Info->gpsLatitude[4] = (int) latitude;
        exifApp1Info->gpsLatitude[5] = 10000;
        // Longtitude
        exifApp1Info->gpsLongitude[0] = (int) longitude;
        exifApp1Info->gpsLongitude[1] = 1;
        longitude -= exifApp1Info->gpsLongitude[0];
        longitude *= 60;
        exifApp1Info->gpsLongitude[2] = (int) longitude;
        exifApp1Info->gpsLongitude[3] = 1;
        longitude -= exifApp1Info->gpsLongitude[2];
        longitude *= 60;
        longitude *= 10000;
        exifApp1Info->gpsLongitude[4] = (int) longitude;
        exifApp1Info->gpsLongitude[5] = 10000;

        // Timestamp
        if ( timestamp >= 0 )
        {
            time_t tim = (time_t) timestamp;
            struct tm *ptime = gmtime(&tim);
            if (ptime != NULL) {
                exifApp1Info->gpsTimeStamp[0] = ptime->tm_hour;
                exifApp1Info->gpsTimeStamp[1] = 1;
                exifApp1Info->gpsTimeStamp[2] = ptime->tm_min;
                exifApp1Info->gpsTimeStamp[3] = 1;
                exifApp1Info->gpsTimeStamp[4] = ptime->tm_sec;
                exifApp1Info->gpsTimeStamp[5] = 1;
                if (0 > snprintf((char *)exifApp1Info->gpsDateStamp, sizeof(exifApp1Info->gpsDateStamp),
                    "%04d:%02d:%02d", ptime->tm_year + 1900, ptime->tm_mon + 1, ptime->tm_mday)
                ) {
                    MY_LOGW("snprintf failed");
                }
            } else {
                MY_LOGD("gmtime might not be implemented");
            }
        }
        else
        {
            MY_LOGE("wrong timestamp(%lld)", timestamp);
        }
        // ProcessingMethod
        const char exifAsciiPrefix[] = { 0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0 }; // ASCII
        int len1, len2, maxLen;
        len1 = sizeof(exifAsciiPrefix);
        memcpy(exifApp1Info->gpsProcessingMethod, exifAsciiPrefix, len1);
        maxLen = sizeof(exifApp1Info->gpsProcessingMethod) - len1;
        len2 = strlen(pgpsProcessingMethod);
        if (len2 > maxLen) {
            len2 = maxLen;
        }
        memcpy(&exifApp1Info->gpsProcessingMethod[len1], pgpsProcessingMethod, len2);
    }

    /*********************************************************************************
                                           common
    **********************************************************************************/
    // software information
    memset(exifApp1Info->strSoftware, 0, 32);
    char _strSoftware[] =  "MediaTek Camera Application";
    strncpy((char *)exifApp1Info->strSoftware, "MediaTek Camera Application", sizeof(_strSoftware)/sizeof(char));

    // get datetime
    // [SubsecTime]
    // convert microsecond to second
    // if microsecond is not full 6 digits, it will be filled with "0" from head
    struct tm *tm;
    if ((tm = localtime(&mExifParam.seconds)) != NULL)
    {
        size_t stret = strftime((char *)exifApp1Info->strDateTime,
           sizeof(exifApp1Info->strDateTime), "%Y:%m:%d %H:%M:%S", tm);
        if (stret == 0) {
            MY_LOGE("strftime failed!");
        }
        int snpret = 0;
        snpret = snprintf((char *)exifApp1Info->strSubSecTime,
            sizeof(exifApp1Info->strSubSecTime), "%06ld\n", mExifParam.microSeconds);
        if (snpret < 0) {
            MY_LOGE("snprintf failed!");
        }
        MY_LOGI("strDateTime(%s), strSubSecTime(%s)", exifApp1Info->strDateTime, exifApp1Info->strSubSecTime);
    }

    // [digital zoom ratio]
    exifApp1Info->digitalZoomRatio[0] = (unsigned int)mExifParam.u4ZoomRatio;
    exifApp1Info->digitalZoomRatio[1] = 100;
    // [orientation]
    exifApp1Info->orientation = (unsigned short)determineExifOrientation(
                                    mExifParam.u4Orientation,
                                    mExifParam.u4Facing
                                );

    /*********************************************************************************
                                           3A
    **********************************************************************************/
    // [f number]
    //
    // [max aperture value]
    // aperture value = 2 log2(f number)
    exifApp1Info->fnumber[0] = (unsigned int)mExifParam.u4FNumber;
    exifApp1Info->fnumber[1] = FNUMBER_PRECISION;
    double apertureValue = 2 * log(mExifParam.u4FNumber / FNUMBER_PRECISION) / log(2);
    exifApp1Info->maxApertureValue[0] = static_cast<unsigned int>(apertureValue * 100);
    exifApp1Info->maxApertureValue[1] = 100;

    // [focal length]
    exifApp1Info->focalLength[0] = (unsigned int)mExifParam.u4FocalLength;
    exifApp1Info->focalLength[1] = 1000;

    // [35mm focal length]
    exifApp1Info->focalLength35mm = (unsigned short)mExifParam.u4FocalLength35mm;

    // [iso speed]
    // For super high ISO case(>65535), the isoSpeedRatings will be equal to 65535 due to its SHORT type
    // Therefore, we record real ISO value in another variable isoSpeed, which is also EXIF_TAG_RECOMMENDEDEXPOSUREINDEX
    // isoType indicates the tag we use for recording real ISO value, and we use EXIF_TAG_RECOMMENDEDEXPOSUREINDEX here which isoType = 2
    // isoType: 1=StandardOutputSensitivity, 2=RecommendedExposureIndex, 3=ISOSpeed
    if ( mExifParam.u4AEISOSpeed > 65535) {
        exifApp1Info->isoSpeedRatings = 65535;
        exifApp1Info->isoType = 2;
        exifApp1Info->isoSpeed = (unsigned int)mExifParam.u4AEISOSpeed;
    } else {
        exifApp1Info->isoSpeedRatings = (unsigned short)mExifParam.u4AEISOSpeed;
    }

    // [exposure time]
    //
    // [shutter speed value]
    // shutterspeedvalue = -log2(exp time)
    if(mExifParam.u4CapExposureTime == 0){
        //YUV sensor
        exifApp1Info->exposureTime[0] = 0;
        exifApp1Info->exposureTime[1] = 0;
        exifApp1Info->shutterSpeed[0] = 0;
        exifApp1Info->shutterSpeed[1] = 0;
    }
    else{
        // RAW sensor
        if (mExifParam.u4CapExposureTime > 1000000) { //1 sec
            exifApp1Info->exposureTime[0] = mExifParam.u4CapExposureTime / 100000;
            exifApp1Info->exposureTime[1] = 10;
        }
        else{ // us
            exifApp1Info->exposureTime[0] = mExifParam.u4CapExposureTime;
            exifApp1Info->exposureTime[1] = 1000000;
        }
        double shutterSpeedValue = log(1000000/mExifParam.u4CapExposureTime) / log(2);
        exifApp1Info->shutterSpeed[0] = static_cast<unsigned int>(shutterSpeedValue * 1000);
        exifApp1Info->shutterSpeed[1] = 1000;
    }


    // [flashlight]
    // bit coding of the exif flash tag
    //         |6|5|4|3|2|1|0|
    //
    // use 0~6 bit determine the flash value
    // 0: indicates the flash firing status(on, off)
    // 1,2: indicates the flash return status
    // 3,4: indicates the flash mode(auto, compulsory)
    // 5: indicates the flash function
    // 6: indicates the red eye mode
    exifApp1Info->flash = (mExifParam.u4FlashFiringStatus << FLASH_FIRING_STATUS_SHIFT |
        mExifParam.u4FlashReturnDetect << FLASH_RETURN_DETECT_SHIFT |
        mExifParam.u4FlashMode << FLASH_MODE_SHIFT |
        mExifParam.u4FlashFunction << FLASH_SUPPORT_SHIFT |
        mExifParam.u4FlashRedEye << FLASH_REDEYE_SHIFT);

    // [white balance mode]
    exifApp1Info->whiteBalanceMode = mExifParam.u4AWBMode;

    // [light source]
    exifApp1Info->lightSource = mExifParam.u4LightSource;

    // [metering mode]
    exifApp1Info->meteringMode = mExifParam.u4AEMeterMode;

    // [exposure program] , [scene mode]
    exifApp1Info->exposureProgram  = mExifParam.u4ExpProgram;
    exifApp1Info->sceneCaptureType = mExifParam.u4SceneCapType;

    // [Brightness value]
    exifApp1Info->brightness[0] = mExifParam.i4Brightness;
    exifApp1Info->brightness[1] = 10;

    // [Ev offset]
    exifApp1Info->exposureBiasValue[0] = (unsigned int)mExifParam.i4AEExpBias;
    exifApp1Info->exposureBiasValue[1] = 10;

    /*********************************************************************************
                                           update customized exif
    **********************************************************************************/
    {
        char make[PROPERTY_VALUE_MAX] = {'\0'};
        char model[PROPERTY_VALUE_MAX] = {'\0'};
        property_get("ro.product.manufacturer", make, "0");
        property_get("ro.product.model", model, "0");
        MY_LOGI("property: make(%s), model(%s)", make, model);
        // [Make]
        if ( ::strcmp(make, "0") != 0 ) {
            ::memset(exifApp1Info->strMake, 0, 32);
            ::strncpy((char*)exifApp1Info->strMake, (const char*)make, 31);
        }
        // [Model]
        if ( ::strcmp(model, "0") != 0 ) {
            ::memset(exifApp1Info->strModel, 0, 32);
            ::strncpy((char*)exifApp1Info->strModel, (const char*)model, 31);
        }
    }

    /*********************************************************************************
                                           MISC
    **********************************************************************************/
    // [flashPixVer]
    memcpy(exifApp1Info->strFlashPixVer, "0100 ", 5);
    // [exposure mode]
    exifApp1Info->exposureMode = 0;  // 0 means Auto exposure

}

可以看到有GPS、common、3A相关exif信息被写入,同时还有软件来源_strSoftware[] = "MediaTek Camera Application"以及最后的自定义系统属性等。

(3)App如何修改Exif信息

Exif信息的Tag都在如下类中定义:

//frameworks/base/media/java/android/media/ExifInterface.java

public static final String TAG_ORIENTATION = "Orientation"; 方向
public static final String TAG_DATETIME = "DateTime"; 时间日期
public static final String TAG_MAKE = "Make"; 设备制造商
public static final String TAG_MODEL = "Model"; 设备型号
public static final String TAG_FLASH = "Flash"; 闪光灯
public static final String TAG_IMAGE_WIDTH = "ImageWidth"; 图片宽
public static final String TAG_IMAGE_LENGTH = "ImageLength"; 图片长
public static final String TAG_GPS_LATITUDE = "GPSLatitude"; 纬度
public static final String TAG_GPS_LONGITUDE = "GPSLongitude"; 经度
public static final String TAG_EXPOSURE_TIME = "ExposureTime"; 曝光时间
public static final String TAG_APERTURE = "FNumber"; 光圈值
public static final String TAG_ISO = "ISOSpeedRatings";ISO
//...

应用如何读取和写入Exif信息:

//读取图片EXIF信息
ExifInterface exifInterface=new ExifInterface(imgPath);
String Orientation=exifInterface.getAttribute(ExifInterface.TAG_ORIENTATION);
String DateTime=exifInterface.getAttribute(ExifInterface.TAG_DATETIME);
String Make=exifInterface.getAttribute(ExifInterface.TAG_MAKE);
String Model=exifInterface.getAttribute(ExifInterface.TAG_MODEL);
String Flash=exifInterface.getAttribute(ExifInterface.TAG_FLASH);
int ImageWidth=exifInterface.getAttributeInt(android.media.ExifInterface.TAG_IMAGE_WIDTH, 0);
int ImageLength=exifInterface.getAttributeInt(android.media.ExifInterface.TAG_IMAGE_LENGTH, 0);
String ExposureTime=exifInterface.getAttribute(ExifInterface.TAG_EXPOSURE_TIME);
String FNumber=exifInterface.getAttribute(ExifInterface.TAG_APERTURE);
String ISOSpeedRatings=exifInterface.getAttribute(ExifInterface.TAG_ISO);
//...

//替换写入EXIF信息
android.media.ExifInterface exifInterface = new android.media.ExifInterface(mPath);
exifInterface.setAttribute(android.media.ExifInterface.TAG_IMAGE_WIDTH,"1920");//宽度修改无效
exifInterface.setAttribute(android.media.ExifInterface.TAG_IMAGE_LENGTH,"2560");//高度修改无效
exifInterface.setAttribute(android.media.ExifInterface.TAG_MODEL,"S123456");
exifInterface.setAttribute(android.media.ExifInterface.TAG_ISO,"8000");
exifInterface.saveAttributes();
 类似资料: