Status Update
Comments
ap...@google.com <ap...@google.com> #2
Branch: androidx-main
commit 2db6d01af84b5752190c6dd28cdea12500b701cb
Author: Charcoal Chen <charcoalchen@google.com>
Date: Fri Dec 23 10:15:10 2022
Fix JPEG image corruption issue if writing Exif location data on some Samsung Android 12 devices
This issue can be avoided in CameraX side by skipping the unnecessary Exif data copy. But there should still be some unknown reason in ExifInterface or these problematic devices to cause the issue.
Relnote: "Fixed JPEG image corruption issue if writing Exif location data on some Samsung Android 12 devices."
Bug: 263289024
Test: ImageCaptureTest
Change-Id: Ib70862aa6e654f06b9358e3f92bbb98c86cb9caf
M camera/camera-core/src/main/java/androidx/camera/core/impl/utils/Exif.java
M camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
ap...@google.com <ap...@google.com> #3
Thanks for reporting, i can use the example app to reproduce the problem. I saved the corrupted file and pulled it off the device and ran it through exiftool
, which complained (it doesn't complain as loudly about the source file, just some technically out-of-order tags):
$ exiftool -v2 editedFile.jpg
ExifToolVersion = 12.52
FileName = editedFile.jpg
Directory = .
FileSize = 433785
FileModifyDate = 1678109782
FileAccessDate = 1678109782
FileInodeChangeDate = 1678109782
FilePermissions = 33184
FileType = JPEG
FileTypeExtension = JPG
MIMEType = image/jpeg
Warning = [minor] Skipped unknown 617 bytes after JPEG APP1 segment
JPEG APP1 (46 bytes):
ExifByteOrder = II
Warning = Short directory size for IFD0 (missing 150 bytes)
| Warning = Bad IFD0 directory
Warning = [minor] Skipped unknown 917 bytes after JPEG NULL segment
JPEG SOI
JPEG APP0 (14 bytes):
+ [BinaryData directory, 9 bytes]
| JFIFVersion = 1 1
| - Tag 0x0000 (2 bytes, int8u[2])
| ResolutionUnit = 0
| - Tag 0x0002 (1 bytes, int8u[1])
| XResolution = 1
| - Tag 0x0003 (2 bytes, int16u[1])
| YResolution = 1
| - Tag 0x0005 (2 bytes, int16u[1])
| ThumbnailWidth = 0
| - Tag 0x0007 (1 bytes, int8u[1])
| ThumbnailHeight = 0
| - Tag 0x0008 (1 bytes, int8u[1])
JPEG DQT (65 bytes):
JPEG DQT (65 bytes):
JPEG SOF0 (15 bytes):
ImageWidth = 512
ImageHeight = 384
EncodingProcess = 0
BitsPerSample = 8
ColorComponents = 3
YCbCrSubSampling = 2 1
JPEG DHT (29 bytes):
JPEG DHT (179 bytes):
JPEG DHT (29 bytes):
JPEG DHT (179 bytes):
JPEG SOS
ma...@google.com <ma...@google.com>
pr...@google.com <pr...@google.com> #4
The size of the JPEG APP1
segment looks wrong in the edited file (from exiftool
):
JPEG APP1 (46 bytes):
Compared to the original file:
JPEG APP1 (65279 bytes):
In binary this difference is visible in the 5th/6th byte of each file (from hexdump -C
). The first 4 bytes are a JPEG marker (0xFF
), a JPEG SOI (start of image, 0xd8
), another marker and an APP1 (0xe1
), followed by 2 bytes indicating the size of the APP1 segment.
Edited image:
ff d8 ff e1 00 30
Original image:
ff d8 ff e1 ff 01
Looking into why ExifInterface
is writing out the wrong APP1 size, I turned on
$ adb shell setprop log.tag.ExifInterface VERBOSE
And now it becomes a bit more obvious (this logging is from
saveJpegAttributes starting with (inputStream: java.io.BufferedInputStream@4fba9e8, outputStream: java.io.BufferedOutputStream@e5c3801)
index: 0, offsets: 8, tag count: 15, data sizes: 81, total size: 65584
65584
(0x10030
) is greater than the max 16-bit unsigned value (2^16 = 65536
), so when we ByteOrderedDataOutputStream.writeUnsignedShort
writeShort((short) val)
) truncates to the lower two bytes (0x0030
) - and this gets written out to the file and then breaks everything.
I think this likely only affects files that are very close to the 2^16
threshold, and the act of 'copying' all the EXIF attributes over probably reformats some of them just enough to take up slightly more bytes.
I'm not sure what is supposed to happen when a JPEG APP1 segment gets larger than 2^16
bytes, I need to dig a bit further into that part.
Description
No description yet.