exiftool not processing entire (large) byte stream when fed through named pipe

Started by Lavoisier, January 15, 2016, 10:37:16 AM

Previous topic - Next topic

Lavoisier

Hi everyone,

First, to Phil: thank you for letting us all benefit from your amazing work on metadata processing.

I am writing a program in C++ (running on a Linux Debian), which uses exiftool (through its C++ API) to process byte streams (this is the input data of my program).

For performance concerns, I want to avoid using files. So I use a named pipe. My program receives a byte stream and writes it into the named pipe by chunks of 64 KB (that is the limit for the named pipes' buffer on Linux), and passes the name of the pipe to the ExifTool instance (by calling exiftool->ImageInfo(name_of_the_pipe)), so that it read and process the byte stream on the other end of the pipe.

The problem is: when processing large byte streams (several hundreds of MB - video files, for example), exiftool stops reading after the first 64 KB chunk, as if it had encountered EOF, and outputs its results. This does not occur for smaller byte streams (pictures of several tens of MB): exiftool reads them all.

I checked that the code which handles pipe operations (open/write/close) is correct. It seems to be: when I replace the call to exiftool's ImageInfo method with a small thread reading the byte stream from the pipe, it works like charm.

I am not familiar with Perl, so I didn't look into exiftool's source code yet.

Would anyone have any idea on this? May it come from the way exiftool handles reading its input data?

Thanks in advance for any comment.
Pierre

Phil Harvey

Have you checked the ExifTool warnings?  You may get this warning for some large video files:

End of processing at large atom (LargeFileSupport not enabled)

If this is the problem, enabling LargeFileSupport may work, but setting LargeFileSupport when using the C++ interface may be tricky.  I recommend setting it via the via the UserDefined::Options in the config file (see here).

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

Lavoisier

Hi Phil,
Thank you for the quick reply.

Quote from: Phil Harvey on January 15, 2016, 11:20:05 AM
Have you checked the ExifTool warnings?  You may get this warning for some large video files:

Do you mean errors (returned by method GetError() of ExifTool) ? Or is there something else, showing warnings, that I am missing?
GetError() returns nothing.

Pierre

Phil Harvey

Hi Pierre,

Ah.  GetError() returns the stderr output.  Yes, I would have expected to see the warning here.  Try running the command-line version on a file that gives you problems.

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

Lavoisier


Lavoisier

Quote from: Phil Harvey on January 15, 2016, 01:34:27 PM
Try running the command-line version on a file that gives you problems.

Done that. Works like a charm...  :(

Phil Harvey

Odd.  I can't see how your named pipe should be able to tell if the file is huge, unless exiftool doesn't like the huge file for another reason.

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

Lavoisier

Yes, very odd. Time is up now on this side of the Atlantic. I will show some code next week.
Thanks for your help so far. Have a good week-end.
Pierre

Lavoisier

PS: I have tried the LargeFileSupport, of course. It doesn't change anything.

Lavoisier

Hi Phil,
I have good news. I have extracted (in a test program) the code that sends the byte stream through the named pipe to exiftool, and it works just fine. Yep, I should have started by checking that before calling out for your help.  :-[
Sorry to have bothered you with my mistake - unidentified so far. Thank you for your answers.
Pierre

Phil Harvey

...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

Lavoisier

Hi Phil,
Actually... My conclusion was wrong, yesterday, as I had not checked my test program against every file type I have (that is: avi, flv, jpeg, text and mp4 files).

Everything works fine except for flash video files (and I was initially processing flash video files).

When processing a byte stream coming from a flash video file, writing on the named pipe always hangs after exactly 73,728 bytes (72 * 1024), as if exiftool had stopped reading from the pipe (and the pipe is full). Yet, exiftool returns all the metadata that is expected for the file.

I doubt it, but is there some limitation in exiftool's code on how much is processed from flash video files (someting similar to -fast[NUM] option)? I quickly grepped into the source, but found nothing obvious.

N.B. : I tested my code writing in the named pipe one byte at a time (instead of 65,535 byte chunks). exiftool exits with an error (et->LastComplete < 0) at around 6.156 bytes read (this value varies +/- 10 bytes with executions), but et->GetError() returns nothing.

Pierre

Lavoisier

Hi Phil,
Here is my code, based on your example1.cpp embedded with cpp_exiftool.tar.gz (C++ API), and, attached to this post, a zip file with the source files and a Makefile to compile.
If needed, small flash video files are available here: http://www.mediacollege.com/adobe/flash/video/tutorial/example-flv.html

main.cpp:
#include <stdio.h>
#include <iostream>
#include <vector>

#include <cstdlib>
#include <sys/stat.h>

#include <errno.h>
#include <fcntl.h>
#include <unistd.h>

#include "ExifTool.h"
#include "Writer.hpp"

#include <QFile>
#include <QByteArray>

using namespace std;

#define PIPE_NAME "myPipe"

int main(int argc, char **argv)
{
    int ret;
    if (argc < 2) {
        cout << "--- process a file with exiftool through a named pipe ---" << endl;
        cout << "I need the name of the file you want to process." << endl;
        return 1;
    }
    cout << "[main] start" << endl;

    // create our ExifTool object
    ExifTool *et = new ExifTool();

    // get file content into a byte array
    QFile file(argv[1]);
    if (!file.open(QIODevice::ReadOnly)) {
cerr << "[main] error opening file " << argv[1] << endl;
return 1;
    } else cout << "[main] processing file " << argv[1] << endl;
    QByteArray inputData = file.readAll();
    cout << "[main] inputData.size() = " << inputData.size() << " byte" << (inputData.size() > 1 ? "s" : "") << endl;

    // create named pipe
    char* pipe = const_cast<char *>(PIPE_NAME);
    struct stat buffer;
    // If pipe already exists, remove it
    if (stat(pipe, &buffer) == 0) {
ret = unlink(pipe); // remove pipe
if (ret != 0) {
cerr << "[main] pipe named " << pipe << " already exists, and cannot be removed!" << endl;
cerr << "[main] aborted" << endl;
return 1;
}
    }
    ret = mkfifo(pipe, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
    if (ret != 0) {
cerr << "[main] error creating pipe named " << pipe << " (return code: " << ret << " - " << strerror(errno) << ")" << endl;
cerr << "[main] aborted" << endl;
return 1;
    }

    // open pipe for reading
    int fd;
    if ((fd = open(pipe, O_RDONLY|O_NONBLOCK)) < 0) {
cerr << "[main] error opening pipe named " << pipe << " (O_RDONLY and O_NONBLOCK) - " << strerror(errno) << endl;
cerr << "[main] aborted" << endl;
return 1;
    }

    // start writing
    Writer* w = new Writer(inputData, pipe);
    cout << "[main] start writing thread" << endl;
    w->start();

    // read metadata from the image
    cout << "[main] call to ImageInfo (reads from named pipe)" << endl;
    TagInfo *info = et->ImageInfo(pipe,NULL,5);
    if (info) {
cout << "[main] -" << endl;
cout << "[main] exiftool has finished and returned the following meta information :" << endl;
        // print returned information
        for (TagInfo *i=info; i; i=i->next) {
            cout << "[main] \\_ " << i->name << " = " << i->value << endl;
        }
cout << "[main] -" << endl;
        // we are responsible for deleting the information when done
        delete info;
    }
    if (et->LastComplete() <= 0) cerr << "[main] ## error executing exiftool! ##" << endl;
    // print exiftool stderr messages
    char *err = et->GetError();
    if (err) cerr << err;
    else if (et->LastComplete() <= 0) cerr << "[main] ## no error message! ##" << endl;
    ret = unlink(pipe); // suppression du pipe
    if (ret != 0) {
      cerr << "[main] error removing pipe named : " << pipe << " (return code: " << ret << " - " << strerror(errno) << ")" << endl;
      return 1;
    }
    cout << "[main] deleting Exiftool object..." << endl;
    delete et; // delete our ExifTool object
    cout << "[main] Exiftool object deleted" << endl;
    cout << "[main] -- end of execution --" << endl;
    return 0;
}
// end


Writer.cpp:
#include "Writer.hpp"
#include <stdio.h>
#include <iostream>
#include<fstream>
#include<iomanip>
#include<cmath>

#define FIFO_BUFFER_MAX_SIZE 65535
#define MAX_WRITE_ERRORS 1000
#define WITNESS_FILE_NAME "witness"

using namespace std;

Writer::Writer(const QByteArray& inputData, char* pipe) : m_inputData(inputData), m_pipe(pipe), QThread() {}

void Writer::run() {
    int nbBytesWritten, nbBytesWrittenForWitness, fd, ret;
    int nbWrittenBytesTotal = 0, nbCycles = 0, nbWriteErrors = 0;
    int byteStreamSize = m_inputData.size();
    int nbCyclesTheoritical = byteStreamSize / FIFO_BUFFER_MAX_SIZE + ((byteStreamSize % FIFO_BUFFER_MAX_SIZE) > 0 ? 1 : 0);
    int nbLeadingSpacesCycles = log10(nbCyclesTheoritical) + 1; // used for formatting logs
    int nbLeadingSpacesBytes = log10(byteStreamSize) + 1; // used for formatting logs
    cout << "[Writer] processing a " << byteStreamSize << " byte stream (theorically in " << nbCyclesTheoritical << " write cycle" << (nbCyclesTheoritical > 1 ? "s)" : ")") << endl;

    // open "witness" file (used to check that the byte stream is strictly identical to the content of the file being processed
    FILE *file = fopen(WITNESS_FILE_NAME, "w");
    if (file == NULL) {
cerr << "[Writer] error opening file for writing - resuming execution" << endl;
    }

    // open pipe for writing
    if ((fd = open(m_pipe, O_WRONLY)) < 0) {
       cout << "[Writer] error opening pipe named " << m_pipe << " (O_WRONLY) - " << strerror(errno) << endl;
       return;
    }

    // write loop
    while (true) {
// Write next segment of FIFO_BUFFER_MAX_SIZE bytes
if (nbWrittenBytesTotal < byteStreamSize) {
nbCycles++;
cout << "[Writer] \\_ cycle " << std::setw(nbLeadingSpacesCycles) << std::setfill(' ') << nbCycles << " / " << std::setw(nbLeadingSpacesCycles) << std::setfill(' ') << nbCyclesTheoritical << ends;
cout << ": writing segment [" << std::setw(nbLeadingSpacesBytes) << std::setfill(' ') << nbWrittenBytesTotal << ", " << std::setw(nbLeadingSpacesBytes) << std::setfill(' ') << nbWrittenBytesTotal + FIFO_BUFFER_MAX_SIZE - 1 << "]... " << ends;
}
        QByteArray segment = m_inputData.mid(nbWrittenBytesTotal, FIFO_BUFFER_MAX_SIZE);
nbBytesWritten = write(fd, segment.data(), segment.length());
if (nbWrittenBytesTotal < byteStreamSize) cout << "bytes written into pipe: " << nbBytesWritten << endl;
// Write byte stream into "witness" file
int nbBytesWrittenForWitness = fwrite(segment.data(), sizeof(char), segment.length(), file);
// check number of bytes actually written into pipe
        if (nbBytesWritten < 0) {
    if (++nbWriteErrors > MAX_WRITE_ERRORS) {
cerr << "[Writer] \\_ cycle " << std::setw(nbLeadingSpacesCycles) << std::setfill(' ') << nbCycles << " / " << std::setw(5) << std::setfill(' ') << nbCyclesTheoritical << ends;
cerr << ": too many errors occurred writing into pipe (" << nbWrittenBytesTotal << " byte" << (nbWrittenBytesTotal > 1 ? "s" : "") << " written in " << nbCycles << " cycle" << (nbCycles > 1 ? "s" : "") << ends;
cerr << " so far) - " << strerror(errno) << " - abort writing" << endl;
break;
    }
    cerr << "[Writer] \\_ cycle " << std::setw(nbLeadingSpacesCycles) << std::setfill(' ') << nbCycles << " / " << std::setw(5) << std::setfill(' ') << nbCyclesTheoritical << ends;
    cerr << ": error writing into pipe (" << nbWrittenBytesTotal << " byte" << (nbWrittenBytesTotal > 1 ? "s" : "") << " written in " << nbCycles << " cycle" << (nbCycles > 1 ? "s" : "") << ends;
    cerr << " so far) - " << strerror(errno) << endl;
        } else {
  if (nbBytesWritten == 0) {
    cout << "[Writer] finished writing into " << m_pipe << " after having sent " << nbWrittenBytesTotal << " byte" << (nbWrittenBytesTotal > 1 ? "s" : "") << " in " << nbCycles << " cycle" << (nbCycles > 1 ? "s" : "") << endl;
    break;
  }
  nbWrittenBytesTotal += nbBytesWritten;
}
    }
    if ((ret = close(fd)) < 0) cout << "[Writer] error closing pipe (O_WRONLY): " << strerror(errno) << endl;
    if ((ret = fclose(file)) < 0) cerr << "[Writer] error closing file: " << strerror(errno) << endl;
}


Writer.hpp
#ifndef WRITER_H
#define WRITER_H

#include <QThread>
#include <stdio.h>
#include <iostream>
#include <vector>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <QByteArray>

class Writer : public QThread {

    Q_OBJECT

public:
    Writer(const QByteArray& inputData, char* pipe);

private:
    void run();
    QByteArray m_inputData;
    char* m_pipe;
};

#endif // WRITER_H


Makefile, generated with qmake (API source files stored in the same directory as my program files):
#############################################################################
# Makefile for building: test_named_pipe
# Generated by qmake (2.01a) (Qt 4.8.2)
# Project:  test_named_pipe.pro
# Template: app
# Command: /usr/bin/qmake -o Makefile test_named_pipe.pro
#############################################################################

####### Compiler, tools and options

CC            = gcc
CXX           = g++
DEFINES       = -DQT_WEBKIT -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED
CFLAGS        = -m64 -pipe -O2 -Wall -W -D_REENTRANT $(DEFINES)
CXXFLAGS      = -m64 -pipe -O2 -Wall -W -D_REENTRANT $(DEFINES)
INCPATH       = -I/usr/share/qt4/mkspecs/linux-g++-64 -I. -I/usr/include/qt4/QtCore -I/usr/include/qt4/QtGui -I/usr/include/qt4 -I. -I.
LINK          = g++
LFLAGS        = -m64 -Wl,-O1
LIBS          = $(SUBLIBS)  -L/usr/lib/x86_64-linux-gnu -lQtGui -lQtCore -lpthread
AR            = ar cqs
RANLIB        =
QMAKE         = /usr/bin/qmake
TAR           = tar -cf
COMPRESS      = gzip -9f
COPY          = cp -f
SED           = sed
COPY_FILE     = $(COPY)
COPY_DIR      = $(COPY) -r
STRIP         = strip
INSTALL_FILE  = install -m 644 -p
INSTALL_DIR   = $(COPY_DIR)
INSTALL_PROGRAM = install -m 755 -p
DEL_FILE      = rm -f
SYMLINK       = ln -f -s
DEL_DIR       = rmdir
MOVE          = mv -f
CHK_DIR_EXISTS= test -d
MKDIR         = mkdir -p

####### Output directory

OBJECTS_DIR   = ./

####### Files

SOURCES       = ExifTool.cpp \
ExifToolPipe.cpp \
main.cpp \
TagInfo.cpp \
Writer.cpp moc_Writer.cpp
OBJECTS       = ExifTool.o \
ExifToolPipe.o \
main.o \
TagInfo.o \
Writer.o \
moc_Writer.o
DIST          = /usr/share/qt4/mkspecs/common/unix.conf \
/usr/share/qt4/mkspecs/common/linux.conf \
/usr/share/qt4/mkspecs/common/gcc-base.conf \
/usr/share/qt4/mkspecs/common/gcc-base-unix.conf \
/usr/share/qt4/mkspecs/common/g++-base.conf \
/usr/share/qt4/mkspecs/common/g++-unix.conf \
/usr/share/qt4/mkspecs/qconfig.pri \
/usr/share/qt4/mkspecs/modules/qt_webkit_version.pri \
/usr/share/qt4/mkspecs/features/qt_functions.prf \
/usr/share/qt4/mkspecs/features/qt_config.prf \
/usr/share/qt4/mkspecs/features/exclusive_builds.prf \
/usr/share/qt4/mkspecs/features/default_pre.prf \
/usr/share/qt4/mkspecs/features/release.prf \
/usr/share/qt4/mkspecs/features/default_post.prf \
/usr/share/qt4/mkspecs/features/unix/gdb_dwarf_index.prf \
/usr/share/qt4/mkspecs/features/warn_on.prf \
/usr/share/qt4/mkspecs/features/qt.prf \
/usr/share/qt4/mkspecs/features/unix/thread.prf \
/usr/share/qt4/mkspecs/features/moc.prf \
/usr/share/qt4/mkspecs/features/resources.prf \
/usr/share/qt4/mkspecs/features/uic.prf \
/usr/share/qt4/mkspecs/features/yacc.prf \
/usr/share/qt4/mkspecs/features/lex.prf \
/usr/share/qt4/mkspecs/features/include_source_dir.prf \
test_named_pipe.pro
QMAKE_TARGET  = test_named_pipe
DESTDIR       =
TARGET        = test_named_pipe

first: all
####### Implicit rules

.SUFFIXES: .o .c .cpp .cc .cxx .C

.cpp.o:
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<"

.cc.o:
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<"

.cxx.o:
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<"

.C.o:
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<"

.c.o:
$(CC) -c $(CFLAGS) $(INCPATH) -o "$@" "$<"

####### Build rules

all: Makefile $(TARGET)

$(TARGET):  $(OBJECTS) 
$(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(OBJCOMP) $(LIBS)

Makefile: test_named_pipe.pro  /usr/share/qt4/mkspecs/linux-g++-64/qmake.conf /usr/share/qt4/mkspecs/common/unix.conf \
/usr/share/qt4/mkspecs/common/linux.conf \
/usr/share/qt4/mkspecs/common/gcc-base.conf \
/usr/share/qt4/mkspecs/common/gcc-base-unix.conf \
/usr/share/qt4/mkspecs/common/g++-base.conf \
/usr/share/qt4/mkspecs/common/g++-unix.conf \
/usr/share/qt4/mkspecs/qconfig.pri \
/usr/share/qt4/mkspecs/modules/qt_webkit_version.pri \
/usr/share/qt4/mkspecs/features/qt_functions.prf \
/usr/share/qt4/mkspecs/features/qt_config.prf \
/usr/share/qt4/mkspecs/features/exclusive_builds.prf \
/usr/share/qt4/mkspecs/features/default_pre.prf \
/usr/share/qt4/mkspecs/features/release.prf \
/usr/share/qt4/mkspecs/features/default_post.prf \
/usr/share/qt4/mkspecs/features/unix/gdb_dwarf_index.prf \
/usr/share/qt4/mkspecs/features/warn_on.prf \
/usr/share/qt4/mkspecs/features/qt.prf \
/usr/share/qt4/mkspecs/features/unix/thread.prf \
/usr/share/qt4/mkspecs/features/moc.prf \
/usr/share/qt4/mkspecs/features/resources.prf \
/usr/share/qt4/mkspecs/features/uic.prf \
/usr/share/qt4/mkspecs/features/yacc.prf \
/usr/share/qt4/mkspecs/features/lex.prf \
/usr/share/qt4/mkspecs/features/include_source_dir.prf \
/usr/lib/x86_64-linux-gnu/libQtGui.prl \
/usr/lib/x86_64-linux-gnu/libQtCore.prl
$(QMAKE) -o Makefile test_named_pipe.pro
/usr/share/qt4/mkspecs/common/unix.conf:
/usr/share/qt4/mkspecs/common/linux.conf:
/usr/share/qt4/mkspecs/common/gcc-base.conf:
/usr/share/qt4/mkspecs/common/gcc-base-unix.conf:
/usr/share/qt4/mkspecs/common/g++-base.conf:
/usr/share/qt4/mkspecs/common/g++-unix.conf:
/usr/share/qt4/mkspecs/qconfig.pri:
/usr/share/qt4/mkspecs/modules/qt_webkit_version.pri:
/usr/share/qt4/mkspecs/features/qt_functions.prf:
/usr/share/qt4/mkspecs/features/qt_config.prf:
/usr/share/qt4/mkspecs/features/exclusive_builds.prf:
/usr/share/qt4/mkspecs/features/default_pre.prf:
/usr/share/qt4/mkspecs/features/release.prf:
/usr/share/qt4/mkspecs/features/default_post.prf:
/usr/share/qt4/mkspecs/features/unix/gdb_dwarf_index.prf:
/usr/share/qt4/mkspecs/features/warn_on.prf:
/usr/share/qt4/mkspecs/features/qt.prf:
/usr/share/qt4/mkspecs/features/unix/thread.prf:
/usr/share/qt4/mkspecs/features/moc.prf:
/usr/share/qt4/mkspecs/features/resources.prf:
/usr/share/qt4/mkspecs/features/uic.prf:
/usr/share/qt4/mkspecs/features/yacc.prf:
/usr/share/qt4/mkspecs/features/lex.prf:
/usr/share/qt4/mkspecs/features/include_source_dir.prf:
/usr/lib/x86_64-linux-gnu/libQtGui.prl:
/usr/lib/x86_64-linux-gnu/libQtCore.prl:
qmake:  FORCE
@$(QMAKE) -o Makefile test_named_pipe.pro

dist:
@$(CHK_DIR_EXISTS) .tmp/test_named_pipe1.0.0 || $(MKDIR) .tmp/test_named_pipe1.0.0
$(COPY_FILE) --parents $(SOURCES) $(DIST) .tmp/test_named_pipe1.0.0/ && $(COPY_FILE) --parents ExifTool.h ExifToolPipe.h TagInfo.h Writer.hpp .tmp/test_named_pipe1.0.0/ && $(COPY_FILE) --parents ExifTool.cpp ExifToolPipe.cpp main.cpp TagInfo.cpp Writer.cpp .tmp/test_named_pipe1.0.0/ && (cd `dirname .tmp/test_named_pipe1.0.0` && $(TAR) test_named_pipe1.0.0.tar test_named_pipe1.0.0 && $(COMPRESS) test_named_pipe1.0.0.tar) && $(MOVE) `dirname .tmp/test_named_pipe1.0.0`/test_named_pipe1.0.0.tar.gz . && $(DEL_FILE) -r .tmp/test_named_pipe1.0.0


clean:compiler_clean
-$(DEL_FILE) $(OBJECTS)
-$(DEL_FILE) *~ core *.core


####### Sub-libraries

distclean: clean
-$(DEL_FILE) $(TARGET)
-$(DEL_FILE) Makefile


check: first

mocclean: compiler_moc_header_clean compiler_moc_source_clean

mocables: compiler_moc_header_make_all compiler_moc_source_make_all

compiler_moc_header_make_all: moc_Writer.cpp
compiler_moc_header_clean:
-$(DEL_FILE) moc_Writer.cpp
moc_Writer.cpp: Writer.hpp
/usr/bin/moc-qt4 $(DEFINES) $(INCPATH) Writer.hpp -o moc_Writer.cpp

compiler_rcc_make_all:
compiler_rcc_clean:
compiler_image_collection_make_all: qmake_image_collection.cpp
compiler_image_collection_clean:
-$(DEL_FILE) qmake_image_collection.cpp
compiler_moc_source_make_all:
compiler_moc_source_clean:
compiler_uic_make_all:
compiler_uic_clean:
compiler_yacc_decl_make_all:
compiler_yacc_decl_clean:
compiler_yacc_impl_make_all:
compiler_yacc_impl_clean:
compiler_lex_make_all:
compiler_lex_clean:
compiler_clean: compiler_moc_header_clean

####### Compile

ExifTool.o: ExifTool.cpp ExifTool.h \
ExifToolPipe.h \
TagInfo.h
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o ExifTool.o ExifTool.cpp

ExifToolPipe.o: ExifToolPipe.cpp ExifToolPipe.h
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o ExifToolPipe.o ExifToolPipe.cpp

main.o: main.cpp ExifTool.h \
ExifToolPipe.h \
TagInfo.h \
Writer.hpp
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o main.o main.cpp

TagInfo.o: TagInfo.cpp TagInfo.h
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o TagInfo.o TagInfo.cpp

Writer.o: Writer.cpp Writer.hpp
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o Writer.o Writer.cpp

moc_Writer.o: moc_Writer.cpp
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o moc_Writer.o moc_Writer.cpp

####### Install

install:   FORCE

uninstall:   FORCE

FORCE:


Phil Harvey

I don't have time to test this for quite a while (very busy this week, and on vacation next week), but ExifTool certainly stops reading some file types after it obtains the metadata.  This is true for file formats where the metadata is at the start of the file.

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

Lavoisier

Hi Phil,
Thanks for you answer. Maybe this whole thing boils down to Flash video metadata residing at the beginning of the file... Let's wait until you return.
Have a good vacation time.
Pierre