I have a set of mp4 files that I need to load onto an SD Card and read in my Android app.
The app needs to be able to search the videos by category, so my plan was to add some category info in the mp4 metadata before loading them (probably in the "description" field) and then use a ManagedQuery on the MediaStore.Video.Media.EXTERNAL_CONTENT_URI to find them.
I have updated the "description" field using Adobe Bridge, but when I look at the tags returned by a search, the "description" field is always null. Clearly, the data I'm writing to the mp4 files is not being picked up when Android looks at the video file.
Is there another way I should be writing/searching video metadata?
You should probably look at MediaStore.Video.VideoColumns.DESCRIPTION.
You can query the MediaStore.Video.Media.EXTERNAL_CONTENT_URI as you would any other content provider: http://developer.android.com/guide/topics/providers/content-providers.html#querying
You can add/delete/lookup metadata fields in MP4 files using JCodec's MetadataEditor class.
If you want to add metadata field to MP4 file you can use:
MetadataEditor mediaMeta = MetadataEditor.createFrom(new
File("file.mp4"));
Map<String, MetaValue> meta = mediaMeta.getKeyedMeta();
meta.put("com.android.capture.fps", MetaValue.createFloat(25.));
mediaMeta.save(false); // fast mode is off
Alternatively the same can be done from the command line with the CLI tool (MetadataEditorMain):
./metaedit -sk com.android.capture.fps,float=25.0 file.mp4
In the Java code you can get the list of metadata like so:
MetadataEditor mediaMeta = MetadataEditor.createFrom(new
File("file.mp4"));
Map<String, MetaValue> meta = mediaMeta.getKeyedMeta();
for (String key : meta.keySet()) {
System.out.println(key + ": " + meta.get(key));
}
OR from the command line:
./metaedit <file.mp4>
Read more about it: http://jcodec.org/docs/working_with_mp4_metadata.html
Related
I'm trying to decipher the docs for the Google Drive REST API when it comes to uploading a file. For both iOS and Android, the docs seem to require that the MIME type of the upload be specified in two places: in the metadata for the upload and in the media content itself.
For instance, here's how I've been uploading a file in Android:
final File fileMetadata = new File()
.setName("Sample File.mytikkun")
.setMimeType("application/vnd.zigzagworld.sample")
.setParents(Collections.singletonList(rootFolderId));
final ByteArrayContent content = ByteArrayContent.fromString(
"application/vnd.zigzagworld.sample",
"Sample data");
mDriveService
.files()
.create(fileMetadata, content)
.setFields("id")
.execute();
And here's the same thing in iOS/Swift:
let metaData = GTLRDrive_File()
metaData.name = "Sample File.mytikkun"
metaData.mimeType = "application/vnd.zigzagworld.sample"
metaData.parents = [ prefs.rootFolderId! ]
let content = GTLRUploadParameters(
data: "Sample Data".data(using: String.Encoding.utf8)!,
mimeType: "application/vnd.zigzagworld.sample")
let query = GTLRDriveQuery_FilesCreate.query(
withObject: metaData,
uploadParameters: content)
query.fields = "id"
service.executeQuery(query) { ticket, file, error in
// ...
}
The above codes work, but the repetition of the MIME type seems like a code smell. Is there some way to avoid it?
The two mime types serve different purposes. Drive is capable of converting (say) an MS Excel sheet to a Google Spreadsheet during upload. So the mime type within the File metadata is saying which mime type the resulting Drive file should be. The mime type in the upload request is how Drive should interpret the existing file format.
I would like to set meta data to MP4 file.
I create MP4 file via:
new MediaMuxer(mPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
and file has meta:
Title: unknown
Artist: unknown
Album: unknown
Comment:
...
similar as: Here
I found that for API21+ i could make metadata via: MediaMetadata.Builder but how could i save metadata to created MP4 file?
Without extern library please.
Thank you.
To edit/write metadata, Android SDK doesn´t have any method, probably by copyright issues, but you can use options like:
https://github.com/sannies/mp4parser
http://multimedia.cx/eggs/supplying-ffmpeg-with-metadata/
Probably this is what you are looking for (using FFmpeg):
https://github.com/bytedeco/javacv/blob/master/src/main/java/org/bytedeco/javacv/FFmpegFrameRecorder.java
I'm using mp4parser to mux h264 and aac file which are re-encoded from orginal video file,how can I write the metadata of the original video to the new mp4 file? Or is there a common method to write metadata to mp4 file?
metadata and MP4 is a really problem. There is no generally supported specification. But this is only one part of the problem.
Prob (1): When to write metadata
Prob (2): What to write
Prob (1) is relatively easy to solve: Just extend the DefaultMp4Builder or the FragmentedMp4Builder on your own and override the
protected ParsableBox createUdta(Movie movie) {
return null;
}
with something meaningful. E.g.:
protected ParsableBox createUdta(Movie movie) {
UserDataBox udta = new UserDataBox();
CopyrightBox copyrightBox = new CopyrightBox();
copyrightBox.setCopyright("All Rights Reserved, me, myself and I, 2015");
copyrightBox.setLanguage("eng");
udta.addBox(copyrightBox);
return udta;
}
some people used that to write apple compatible metadata but even though there are some classes in my code I never really figured out what works and what not. You might want to have a look into Apple's specification here
And yes: I'm posting this a year to late.
It seems that the 'mp4parser' library (https://code.google.com/p/mp4parser/), supports writing Metadata to mp4 files in Android. However, I've found there's little-to-no documentation on how to do this, beyond a few examples in their codebase. I've had some luck with the following example, which writes XML metadata into the 'moov/udta/meta' box:
https://github.com/copiousfreetime/mp4parser/blob/master/examples/src/main/java/com/googlecode/mp4parser/stuff/ChangeMetaData.java
If you consider the alternatives you might want to look at JCodec for this purpose. It now has the org.jcodec.movtool.MetadataEditor API (and a matching CLI org.jcodec.movtool.MetadataEditorMain).
Their documentation contains many samples: http://jcodec.org/docs/working_with_mp4_metadata.html
So basically when you want to add some metadata you need to know what key(s) it corresponds to. One way to find out is to inspect a sample file that already has the metadata you need. For this you can run the JCodec's CLI tool that will just print out all the existing metadata fields (keys with values):
./metaedit <file.mp4>
Then when you know the key you want to work with you can either use the same CLI tool:
# Changes the author of the movie
./metaedit -f -si ©ART=New\ value file.mov
or the same thing via the Java API:
MetadataEditor mediaMeta = MetadataEditor.createFrom(new
File("file.mp4"));
Map<Integer, MetaValue> meta = mediaMeta.getItunesMeta();
meta.put(0xa9415254, MetaValue.createString("New value")); // fourcc for '©ART'
mediaMeta.save(false); // fast mode is off
To delete a metadata field from a file:
MetadataEditor mediaMeta = MetadataEditor.createFrom(new
File("file.mp4"));
Map<Integer, MetaValue> meta = mediaMeta.getItunesMeta();
meta.remove(0xa9415254); // removes the '©ART'
mediaMeta.save(false); // fast mode is off
To convert string to integer fourcc you can use something like:
byte[] bytes = "©ART".getBytes("iso8859-1");
int fourcc =
ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getInt();
If you want to edit/delete the android metadata you'll need to use a different set of fucntion (because it's stored differently than iTunes metadata):
./metaedit -sk com.android.capture.fps,float=25.0 file.mp4
OR alternatively the same through the API:
MetadataEditor mediaMeta = MetadataEditor.createFrom(new
File("file.mp4"));
Map<String, MetaValue> meta = mediaMeta.getKeyedMeta();
meta.put("com.android.capture.fps", MetaValue.createFloat(25.));
mediaMeta.save(false); // fast mode is off
The following 2 code snippets demonstrate the problem I ran into with the NEW Google Drive Android API / Google Play Services 4.2 (libver 15). Or is it a feature I don't know about? All error checking and initialization is removed in order to keep the code simple.
1/ I create a picture of "image/jpeg" MIME type;
GoogleApiClient mGAC;
byte[] jpgBuffer;
DriveFolder fldr;
ContentsResult rslt = Drive.DriveApi.newContents(mGAC).await();
Contents cont = rslt.getContents();
cont.getOutputStream().write(jpgBuffer);
MetadataChangeSet meta = new MetadataChangeSet.Builder()
.setTitle("foo.jpg").setMimeType("image/jpeg")
.build();
fldr.createFile(mGAC, meta, cont);
Everything is nice and dandy in the Drive. Image is there, looking great, I can even send it to my mom. But this is not what I'm after. I would like to retrieve it in another part of my Android app.
2/ So I try this:
Query query = new Query.Builder().addFilter(
Filters.eq(SearchableField.MIME_TYPE, "image/jpeg")
).build();
MetadataBufferResult rslt = Drive.DriveApi.query(mGAC, query).await();
for (Metadata md : rslt.getMetadataBuffer()) {
Log.d("TAG", md.getTitle() + " " + md.getMimeType());
}
Result: big, fat NOTHING.
I'm not giving up, so the next logical step is to look for ANY file. And the file "foo.jpg" I saved in step 1/ is showing as "image/png" MIME type.
Mystery is solved. IT IS A FEATURE! The "jpgBuffer" in
cont.getOutputStream().write(jpgBuffer);
actually contains PNG binary data. So even if my app specified "image/jpeg" MIME type, Google after snooping through my (my user's) data decided to correct MIME type to "image/png". It should also be noted that it happens with some delay, making the debugging process even more mysterious.
I have yet another hurdle to climb with my GOOGLE DRIVE SDK Android App. I am uploading scanned images with tightly controlled index fields - user defined 'tags' from local dictionary. For instance XXX.JPG has index words "car" + "insurance". Here is a simplified code snippet:
...
body.setTitle("XXX.JPG");
body.setDescription("car, insurance");
body.setIndexableText(new IndexableText().setText("car insurance"));
body.setMimeType("image/jpeg");
body.setParents(Arrays.asList(new ParentReference().setId(...)));
FileContent cont = new FileContent("image/jpeg", new java.io.File(fullPath("xxx.jpg")));
File gooFl = _svc.files().insert(body, cont).execute();
...
Again, everything works great, except when I start a search, I get results that apparently come from some OCR post process, thus rendering my system's DICTIONARY unusable. I assume I can use a custom MIME type, but then the JPEG images become invisible for users who use standard GOOGLE DRIVE application (local, browser-based ... ). So the question is: Can I upload MIME "image/jpeg" files with custom indexes (either Indexable, or Description fields) but stop GOOGLE from OCR-ing my files and adding indexes I did not intend to have?
Just to be more specific, I search for "car insurance" and instead of my 3 files I indexed this way, I get unmanageable pile of other results (JPEG scanned documents) that had "car" and "insurance" somewhere in them. Not what my app wants.
Thank you in advance, sean
...
Based on Burcu's advise below, I modified my code to something that looks like this (stripped to bare bones):
// define meta-data
File body = new File();
body.setTitle("xxx.jpg");
body.setDescription(tags);
body.setIndexableText(new IndexableText().setText(tags));
body.setMimeType("image/jpeg");
body.setParents(Arrays.asList(new ParentReference().setId(_ymID)));
body.setModifiedDate(DateTime.parseRfc3339(ymdGOO));
FileContent cont =
new FileContent("image/jpeg",new java.io.File(fullPath("xxx.jpg")));
String sID = findOnGOO(driveSvc, body.getTitle());
// file not found on gooDrive, upload and fix the date
if (sID == null) {
driveSvc.files().insert(body, cont).setOcr(false).execute();
driveSvc.files().patch(gooFl.getId(), body).setOcr(false).setSetModifiedDate(true).execute();
// file found on gooDrive - modify metadata and/or body
} else {
// modify content + metadata
if (contentModified) {
driveSvc.files().update(sID, body, cont).setOcr(false).setSetModifiedDate(true).execute();
// only metadata (tags,...)
} else {
driveSvc.files().patch(sID, body).setOcr(false).setSetModifiedDate(true).execute();
}
}
...
It is a block that uploads or modifies a Google Drive file. The two non-standard operations are:
1/ resetting the file's 'modified' date in order to force the date of file creation - tested, works OK
2/ stopping the OCR process that interferes with my apps indexing scheme - will test shortly and update here
For the sake of simplicity, I did not include the implementation of "findInGOO()" method. It is quite simple 2-liner and I can supply it upon request
sean
On insertion, set the ocr parameter to false:
service.files().update(body, content).setOcr(false).execute();