At least one popular ActionScript exif reading library, jp.shichiseki.exif doesn't expect the JFIF marker and fails to find the exif data. The fix is easy, though.
To fix the problem, you have to check to see if the first marker after the initial JPEG marker is the JFIF one. If it is, skip it. That's all there is too it. (Well, at least for the iOS images I tested it on.)
To fix the library, you just have to add the folowing to the ExifInfo class:
private const JFIF_MAKER:Array = [0xff, 0xe0]; //new marker type
//Updated to skip JFIF marker
private function validate(stream:ByteArray):Boolean {
var app1DataSize:uint;
// JPG format check
if (!hasSoiMaker(stream) ) {
return false;
}
if(hasJFIFMaker(stream)) //Skip the JFIF marker, if present. CWW
{
stream.position += 16;
}
else stream.position -=2; //Set position back to start of APP1 marker
if ( !hasAPP1Maker(stream)) {
return false;
}
// handle app1 data size
app1DataSize = stream.readUnsignedShort();
if (!hasExifHeader(stream)) {
return false;
}
return true;
}
private function validate(stream:ByteArray):Boolean {
var app1DataSize:uint;
// JPG format check
if (!hasSoiMaker(stream) ) {
return false;
}
if(hasJFIFMaker(stream)) //Skip the JFIF marker, if present. CWW
{
stream.position += 16;
}
else stream.position -=2; //Set position back to start of APP1 marker
if ( !hasAPP1Maker(stream)) {
return false;
}
// handle app1 data size
app1DataSize = stream.readUnsignedShort();
if (!hasExifHeader(stream)) {
return false;
}
return true;
}
//New function to check for JFIF marker
private function hasJFIFMaker(stream:ByteArray):Boolean {
return compareStreamBytes(stream, JFIF_MAKER);
}
private function hasJFIFMaker(stream:ByteArray):Boolean {
return compareStreamBytes(stream, JFIF_MAKER);
}
Hi Joe, great tutorial!
ReplyDeleteExifLoader.loadFilePromise() doesn't work. ExitInfo is null.
override public function loadFilePromise(promise:IFilePromise, context:LoaderContext=null):void
{
_context = context;
_mediaPromise = promise;
contentLoaderInfo.addEventListener(Event.COMPLETE, onFilePromiseComplete, false, 0, true);
super.loadFilePromise(promise);
}
// FilePromise loaded.
private function onFilePromiseComplete(event:Event):void
{
contentLoaderInfo.removeEventListener(Event.COMPLETE, onFilePromiseComplete);
// Read bytes to populate ExifInfo.
_dataSource = _mediaPromise.open();
if(_mediaPromise.isAsync) {
//trace( "Asynchronous media promise." );
var eventSource:IEventDispatcher = _dataSource as IEventDispatcher;
eventSource.addEventListener(Event.COMPLETE, function():void{ readMediaData(event) }, false, 0, true );
} else {
//trace( "Synchronous media promise." );
readMediaData(event);
}
}
private function readMediaData(event:Event):void
{
var data:ByteArray = new ByteArray();
_dataSource.readBytes( data );
_exif = new ExifInfo(data);
dispatchEvent(event);
}
I suggest dumping data to a file and looking at it in a hex editor. If it is a valid jpeg file and has exif data, then you will have to debug into the exif reader code.
ReplyDeleteI did notice that if anything goes wrong, the jp.shichiseki library returns null for the Exif data.
Thanks Joe.
Delete