|
Hi,
Good pickup - I'm thinking that maybe I should just scan backwards until I find FFD9 , as it has to be there for a valid thumbnail, and it'll be very rare for a thumbnail to be tagged but not valid, so any performance hit for those cases would be acceptable. I can't find any clear indicator of which characters are valid for padding - maybe anything goes.
So does the stream end with just one 00 , or many? Would it be OK for you to PM me a sample image?
Cheers,
Simon
|
|
|
|
|
No idea why you were downvoted - countered.
/ravi
|
|
|
|
|
Hi Simon,
First off, thank you very much for your excellent library and contribution to the community.
I'm having an issue when initializing the ExifReader() constructor in WP8. I'm using the CameraCaptureTask to launch the camera and get the resulting PhotoResult, which I then pass its stream to ExifReader(). However, I always get the following exception:
ExifLib.ExifLibException: Could not find Exif data block
at ExifLib.ExifReader.ReadToExifStart()
at ExifLib.ExifReader..ctor(Stream stream)
Note I'm using the latest ExifLib using NuGet, VS 2012, and WP8 SDK (WP OS 7.1 target).
The phone I'm using to test is Nokia Lumina 920 running WP 8.0.
The exact same code works great on an older Samsung SGH-i917 running WP 7.5
Any help is greatly appreciated!
|
|
|
|
|
Hi,
This is an unfortunate problem in the Windows Phone 8 SDK - the CameraCaptureTask returns a stream of type Microsoft.Phone.Tasks.DssPhotoStream , which returns true for CanSeek , but when you actually try to seek, nothing happens, which of course causes problems within the library when it tries to seek. This doesn't happen on Windows Phone 7, because the WP7 CameraCaptureTask returns an entirely different Stream type.
I've raised an issue on Microsoft Connect about this, but I don't know how far it's progressed.
In the meantime, the best thing you can do is to read the whole stream into a MemoryStream instead, something like this:
var buffer = new byte[photoResult.ChosenPhoto.Length];
photoResult.ChosenPhoto.Read(buffer, 0, buffer.Length);
var ms = new MemoryStream(buffer);
var reader = new ExifReader(ms);
Cheers,
Simon
|
|
|
|
|
Hi Simon,
I appreciate your help and quick response. Unfortunately, the solution you mentioned did not work for me - I still get the same "Cannot read Exif data block" exception.
I have tried reading the stream into various other streams and then pass it to the ExifReader, but all ended up with the same error.
I guess I'll have to wait until MS comes out with a solution for this WP8 issue.
|
|
|
|
|
Hi,
Although you do need the MemoryStream workaround on WP8, I just tested your case in the WP8 emulator, and I found that the stream supplied didn't actually contain any EXIF data. It was missing the APP1 tag (FFE1), where the EXIF data is stored. I further verified this by dumping the file to isolated storage and then extracting it to my PC using <a href="http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh286408%28v=vs.105%29.aspx">IseTool</a> , then viewing "Details" under the properties in Windows Explorer. This is probably what's happening to you, but maybe you could verify this in your code.
A breakpoint on line 349 of ExifReader.cs will tell you the ID of the final tag found (markerStart concatenated with markerNumber ). Page 58 of the EXIF spec will tell you which tag you're looking at. Tags are always in order, so if you're at a tag ID after APP1, you know that APP1 must be missing.
After the first test, I then saved a sample EXIF image from the web into "Saved Pictures" within the emulator and loaded that image using a PhotoChooserTask and the code from my previous post. In this situation, the EXIF data was loaded correctly, so maybe this is just an emulator EXIF issue. Does the same thing happen when debugging on your Lumia?
Note that I don't have a Windows Phone 8 device, so I can't test this with a real phone, but I hope this helps.
Cheers,
Simon
|
|
|
|
|
Hi Simon,
Appreciate the time and effort you took to make this library. I am also having the same crash as mentioned in this post.
I tried all the methods but still for one of the photo my app keep crashing.
Would it be possible to return null in case EXIF data is not found instead of throwing exception?
Regards
Arun S
|
|
|
|
|
Hi Arun,
I'm glad you find the library useful.
If you think you'll be reading from files which lack EXIF data, you should always wrap your instantiation in a try-catch. The thhrowing of an exception from the constructor accords with design guidelines, as it doesn't hide the error from the calling code. It's a genuine error if you try to read EXIF from a file which doesn't contain it.
A final note - you can't return null from a constructor.
I hope this helps.
Cheers,
Simon
|
|
|
|
|
|
Hi Arun,
It sounds like you have 2 issues:
- The stream issue raised in the first post in this thread - just make sure you use a stream which is seekable and for which
Seek() actually works. - The images produced by the app you mentioned may not actually contain EXIF data. Try opening one on your PC to confirm.
if you've fixed issue #1, you'll only get exceptions when the image genuinely lacks the EXIF block.
Cheers,
Simon
|
|
|
|
|
Hi Simon
I also have the same issue on Window 7 & 8 with Nokia Lumia cameras - which does seem to be causing the issue as an HTC phone works correctly.
At this point I am only tring to extract the date/time of the image. The code works perfectly on the HTC but Lumina images, obtained using the stream return of the PhotoChooser never finds the EXIF block and so fails.
However, if I e-mail a photo from the phone and open it on my desktop the EXIF properties are there.
Any thoughts?
|
|
|
|
|
Hi,
If your problem is that the stream can't seek, you may find that you'll have to read the image into a seekable stream first, as shown in this answer:
var buffer = new byte[photoResult.ChosenPhoto.Length];
photoResult.ChosenPhoto.Read(buffer, 0, buffer.Length);
var ms = new MemoryStream(buffer);
var reader = new ExifReader(ms);
It's not a beautiful solution, but until the Lumia returns a seekable stream, there's not much else you can do.
If you find that this stiull doesn't work, try dumping the MemoryStream to a file, then load it up on your PC. It's entirely possible that as in my first answer to this post, your stream really doesn't contain the EXIF block when loaded via the PhotoChooser , in which case there's not much you can do from here, but maybe you could try loading the image straight from the media library youself.
Cheers,
Simon
|
|
|
|
|
Thanks for responding so quickly Simon.
It very much looks like the exif data block cannot be found - I think loading directly may be the way to go.
|
|
|
|
|
Hi Simon,
I downloaded the package and try to compile it but I have a build error which is, I'm afraid, a bit out of my reach.
In ExifReader.cs, in the function GetTagValue, for every call to GetArray() I have an error message.
private bool GetTagValue<T>(Dictionary<ushort, long> tagDictionary, ushort tagID, out T result)
{
...
result = (T) (object) GetArray(tagData, fieldLength, ToUShort;
...
result = (T) (object) GetArray(tagData, fieldLength, ToUint);
...
result = (T) (object) GetArray(tagData, fieldLength, ToURational);
...
}
Error The type arguments for method 'ExifLib.ExifReader.GetArray<t>(byte[], int, ExifLib.ExifReader.ConverterMethod<t>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. D:\WkDev\C#\WkPhotosRenamer\ExifLib\Exiflib\ExifReader.cs 495 47 ExifLib
Do you have an idea?
I am on Visual Studio 2008 on Windows XP 32.
.Net Framework 3.5 is installed and working on my other projets.
Thanks a lot,
Julien
|
|
|
|
|
Hi Julien,
This is because the 3.5 compiler used by Studio 2008 isn't as good at type inference as later compilers - have a look here for more details, but to make this compile in Studio 2008, do this:
In ExifReader.GetTagValue , change all calls to GetArray() to use explicit type arguments, i.e, instead of result = (T) (object) GetArray(tagData, fieldLength, ToUint) , use result = (T) (object) GetArray<uint>(tagData, fieldLength, ToUint)
Alternatively, if you install framework 4.0, you should be able to compile an earlier framework version directly using msbuild:
%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe ExifLibrary.sln /p:TargetFrameworkVersion=v2.0
The nuget package contains versions of the library for all frameworks, so you can also just install using nuget.
Cheers,
Simon
|
|
|
|
|
Hi Simon,
Sorry for my late answer.
As I am forced to stay on Visual 2008 I forced the argument types as you suggested and this works perfectly well
I now face a crash with some kind of jpeg formats (jpeg generated by my HTC phone) but as it is a completely separated problem I'll put it in a separated thread.
Best,
Julien
|
|
|
|
|
Do you have any plan about implementing modification of EXIF data? I want to add gps info because the CameraCaptureTask class just ignore it.
If you are not interesting, can you give me some tips about how to do it? I think that implementing a reverse gettagvalue should work
Cheers,
José
|
|
|
|
|
Hi José,
Editing's quite a different task, and doesn't lend itself well to the design of the library. I recommend just using GDI+ - see this other answer for more details.
Cheers,
Simon
|
|
|
|
|
Thanks for your fast reply, Simon.
My problem is that in Windows Phone there is no GDI support, so I think that the only way I can do this is modifying the stream.
I started to modify the source code of your ExifLib to try to make the job, I created a BinaryWriter and reused a lot of your code, and the new method is very similar to GetTagBytes but when I should read the tagData I just wrote my new data (I respect the offsets if is needed). But my problem is that when I try to read what I wrote with a normal GetValueTag the data is the same as before I started to modify the Exif data.
If you are interested in helping/develop this feature, feel free to contact me and I will share with you the new source code.
Cheer,
José
|
|
|
|
|
Hi José
Good luck with the writer - I'm not personally interested in contributing, but maybe you could start a new project on Sourceforge. I feel that this library exists to perform a single task, and perform it well, i.e. rapid reading of Exif data, but by all means, use the source as a starting point for your writer.
Cheers,
Simon
|
|
|
|
|
Thanks Simon.
Finally I got the writer working, is not as generic as the reader is, but for gps works as expected.
By the way, I noticed that the Dispose method always close the stream, but I think that this is not always the expected behavior. If you use the reader with an stream you probably want to keep the stream openned. I already modified the code to work as I want, but maybe you want to change that in next releases.
Cheers,
José
|
|
|
|
|
Good work, José.
Are you going to release your code somewhere? You make an interesting point about the stream being closed, but if you consider built-in classes like System.IO.BinaryReader , they have the same behaviour on Dispose. However, framework 4.5 adds an overload which allows the stream to be left open afterwards. Perhaps I will add a similar constructor overload to ExifReader .
Cheers,
Simon
|
|
|
|
|
I wasn't planning to release the code because the writter is very specific and it will probably only work with GPS stuff (maybe other rational exif data). The dispose method that i modified is not working as I expected, I need to override the dispose method to be able to just close the binaryreader (I'm not able to call Dispose(bool), as its a protected method).
If you want me to share with you the code, I can give it to you, and do whatever you want.
Cheers,
José
|
|
|
|
|
Hi José,
Just FYI, the latest version of the library contains a constructor overload which allows you to specify whether you want to keep the stream open. Note that this is only available when building with framework 4.5 or higher.
Cheers,
Simon
modified 22-Jul-14 2:40am.
|
|
|
|
|
Thank you Simon,
I'll give it a try, it will help me to avoid opening the same stream many times
|
|
|
|
|