initial implementation of autodetecting mp4 properties

This commit is contained in:
Kai Boernert committed 2017-12-04 11:49:39 +01:00
1 parent 6d5e3e54f6
commit 31690cfdea
15 files changed
+233 -30

No files matched your search

@@ -0,0 +1,28 @@
package com.sap.sailing.domain.common.dto;
import java.util.Date;
public class VideoMetadataDTO {
private boolean canDownload;
private Date recordStartedTime;
private boolean spherical;
public VideoMetadataDTO(boolean canDownload, boolean spherical, Date recordStartedTime) {
this.canDownload = canDownload;
this.spherical = spherical;
this.recordStartedTime = recordStartedTime;
}
public boolean isDownloadable() {
return canDownload;
}
public boolean isSpherical() {
return spherical;
}
public Date getRecordStartedTime() {
return recordStartedTime;
}
}
@@ -55,7 +55,8 @@ Require-Bundle: com.sap.sailing.domain,
com.sap.sse.shared.android,
com.sap.sailing.competitorimport,
com.sap.sailing.expeditionconnector,
com.sap.sailing.expeditionconnector.common
com.sap.sailing.expeditionconnector.common,
org.mp4parser.isoparser;bundle-version="1.9.31"
Bundle-Activator: com.sap.sailing.gwt.ui.server.Activator
Bundle-ActivationPolicy: lazy
Import-Package: javax.servlet;version="3.1.0",
@@ -473,7 +473,7 @@ public class MediaPanel extends FlowPanel implements MediaTracksRefresher {
}
private void addUrlMediaTrack() {
NewMediaWithRaceSelectionDialog dialog = new NewMediaWithRaceSelectionDialog(getDefaultStartTime(),
NewMediaWithRaceSelectionDialog dialog = new NewMediaWithRaceSelectionDialog(mediaService, getDefaultStartTime(),
stringMessages, sailingService, errorReporter, regattaRefresher, regattasDisplayers,
new DialogCallback<MediaTrack>() {
@@ -1,7 +1,14 @@
package com.sap.sailing.gwt.ui.client;
import java.io.IOException;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import com.google.gwt.user.client.rpc.RemoteService;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.dto.VideoMetadataDTO;
import com.sap.sailing.domain.common.media.MediaTrack;
public interface MediaService extends RemoteService {
@@ -26,4 +33,6 @@ public interface MediaService extends RemoteService {
void updateRace(MediaTrack mediaTrack);
VideoMetadataDTO checkMetadata(String url) throws IOException, ParserConfigurationException, SAXException;
}
@@ -2,6 +2,7 @@ package com.sap.sailing.gwt.ui.client;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.dto.VideoMetadataDTO;
import com.sap.sailing.domain.common.media.MediaTrack;
public interface MediaServiceAsync {
@@ -27,5 +28,7 @@ public interface MediaServiceAsync {
void updateDuration(MediaTrack mediaTrack, AsyncCallback<Void> asyncCallback);
void updateRace(MediaTrack mediaTrack, AsyncCallback<Void> asyncCallback);
void checkMetadata(String url, AsyncCallback<VideoMetadataDTO> asyncCallback);
}
@@ -616,7 +616,7 @@ public class MediaPlayerManagerComponent extends AbstractComponent<MediaPlayerSe
public void addMediaTrack() {
TimePoint raceStartTime = getRaceStartTime();
TimePoint defaultStartTime = raceStartTime;
NewMediaDialog dialog = new NewMediaDialog(defaultStartTime, MediaPlayerManagerComponent.this.stringMessages,
NewMediaDialog dialog = new NewMediaDialog(mediaService, defaultStartTime, MediaPlayerManagerComponent.this.stringMessages,
this.getCurrentRace(), new DialogCallback<MediaTrack>() {
@Override
@@ -12,6 +12,8 @@ import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.media.client.Audio;
import com.google.gwt.media.client.MediaBase;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.FocusWidget;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Label;
@@ -21,7 +23,9 @@ import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.dto.VideoMetadataDTO;
import com.sap.sailing.domain.common.media.MediaTrack;
import com.sap.sailing.gwt.ui.client.MediaServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.common.client.YoutubeApi;
import com.sap.sse.common.TimePoint;
@@ -63,13 +67,16 @@ public class NewMediaDialog extends DataEntryDialog<MediaTrack> {
final private RegattaAndRaceIdentifier raceIdentifier;
public NewMediaDialog(TimePoint defaultStartTime, StringMessages stringMessages,
private MediaServiceAsync mediaService;
public NewMediaDialog(MediaServiceAsync mediaService, TimePoint defaultStartTime, StringMessages stringMessages,
RegattaAndRaceIdentifier raceIdentifier, DialogCallback<MediaTrack> dialogCallback) {
super(stringMessages.addMediaTrack(), "", stringMessages.ok(), stringMessages.cancel(), MEDIA_TRACK_VALIDATOR,
dialogCallback);
this.defaultStartTime = defaultStartTime != null ? defaultStartTime : MillisecondsTimePoint.now();
this.stringMessages = stringMessages;
this.raceIdentifier = raceIdentifier;
this.mediaService = mediaService;
registerNativeMethods();
}
@@ -88,7 +95,7 @@ public class NewMediaDialog extends DataEntryDialog<MediaTrack> {
String duration = durationBox.getValue();
if (duration != null && !duration.trim().isEmpty()) {
mediaTrack.duration = TimeFormatUtil.hrsMinSecToMilliSeconds(duration);
} else {
} else {
mediaTrack.duration = null;
}
}
@@ -106,7 +113,7 @@ public class NewMediaDialog extends DataEntryDialog<MediaTrack> {
Date startTime = TimeFormatUtil.DATETIME_FORMAT.parse(startTimeText);
mediaTrack.startTime = new MillisecondsTimePoint(startTime);
} catch (IllegalArgumentException ex) {
//TODO: highlight format error in UI
// TODO: highlight format error in UI
}
}
}
@@ -165,7 +172,7 @@ public class NewMediaDialog extends DataEntryDialog<MediaTrack> {
durationBox = createTextBox(null);
formGrid.setWidget(4, 1, durationBox);
mainPanel.add(formGrid);
return mainPanel;
}
@@ -249,11 +256,12 @@ public class NewMediaDialog extends DataEntryDialog<MediaTrack> {
setTimeout(
function() {
//Remove temporary script element.
if (window != null && window.youtubeMetadataCallbackScript != null) {
document.body
.removeChild(window.youtubeMetadataCallbackScript);
delete window.youtubeMetadataCallbackScript;
if (window != null
&& window.youtubeMetadataCallbackScript != null) {
document.body
.removeChild(window.youtubeMetadataCallbackScript);
delete window.youtubeMetadataCallbackScript;
}
that.@com.sap.sailing.gwt.ui.client.media.NewMediaDialog::setUiEnabled(Z)(true);
}, 2000);
@@ -264,8 +272,7 @@ public class NewMediaDialog extends DataEntryDialog<MediaTrack> {
setUiEnabled(true);
mediaTrack.title = title;
try {
long duration = (long) Math.round(1000 * Double
.valueOf(durationInSeconds));
long duration = (long) Math.round(1000 * Double.valueOf(durationInSeconds));
if (duration > 0) {
mediaTrack.duration = new MillisecondsDurationImpl(duration);
} else {
@@ -285,23 +292,13 @@ public class NewMediaDialog extends DataEntryDialog<MediaTrack> {
infoLabel.setWidget(new Label(mediaTrack.url));
} else {
infoLabelLabel.setText(stringMessages.mimeType() + ":");
if(mediaTrack.mimeType == MimeType.mp4 || mediaTrack.mimeType == MimeType.mp4panorama){
ListBox mimeTypeListBox = createListBox(false);
mimeTypeListBox.addItem(MimeType.mp4.name());
mimeTypeListBox.addItem(MimeType.mp4panorama.name());
infoLabel.setWidget(mimeTypeListBox);
mimeTypeListBox.addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
mediaTrack.mimeType = MimeType.valueOf(mimeTypeListBox.getSelectedValue());
}
});
}else{
if (mediaTrack.mimeType == MimeType.mp4 || mediaTrack.mimeType == MimeType.mp4panorama) {
processMp4(mediaTrack, infoLabel);
} else {
infoLabel.setWidget(new Label(mediaTrack.typeToString()));
}
}
TimePoint startTime = mediaTrack.startTime != null ? mediaTrack.startTime : defaultStartTime;
TimePoint startTime = mediaTrack.startTime != null ? mediaTrack.startTime : defaultStartTime;
String startTimeText = TimeFormatUtil.DATETIME_FORMAT.format(startTime.asDate());
startTimeBox.setText(startTimeText);
@@ -313,6 +310,49 @@ public class NewMediaDialog extends DataEntryDialog<MediaTrack> {
}
}
private void processMp4(MediaTrack mediaTrack, SimplePanel infoLabel2) {
this.setUiEnabled(false);
infoLabel.setWidget(new Label("TODO Please wait, analyzing"));
if (mediaTrack.startTime == null) {
mediaService.checkMetadata(mediaTrack.url, new AsyncCallback<VideoMetadataDTO>() {
@Override
public void onSuccess(VideoMetadataDTO result) {
setUiEnabled(true);
if (!result.isDownloadable()) {
Window.alert("Could not download file " + mediaTrack.url);
manualMimeTypeSelection(mediaTrack);
} else {
mediaTrack.mimeType = result.isSpherical() ? MimeType.mp4panorama : MimeType.mp4;
// check for startime here
}
}
@Override
public void onFailure(Throwable caught) {
setUiEnabled(true);
manualMimeTypeSelection(mediaTrack);
}
});
} else {
manualMimeTypeSelection(mediaTrack);
}
}
private void manualMimeTypeSelection(MediaTrack mediaTrack) {
ListBox mimeTypeListBox = createListBox(false);
mimeTypeListBox.addItem(MimeType.mp4.name());
mimeTypeListBox.addItem(MimeType.mp4panorama.name());
infoLabel.setWidget(mimeTypeListBox);
mimeTypeListBox.addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
mediaTrack.mimeType = MimeType.valueOf(mimeTypeListBox.getSelectedValue());
}
});
}
@Override
protected FocusWidget getInitialFocusWidget() {
return urlBox;
@@ -7,6 +7,7 @@ import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.media.MediaTrack;
import com.sap.sailing.gwt.ui.adminconsole.AssignRacesToMediaDialog;
import com.sap.sailing.gwt.ui.client.MediaServiceAsync;
import com.sap.sailing.gwt.ui.client.RegattaRefresher;
import com.sap.sailing.gwt.ui.client.RegattasDisplayer;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
@@ -29,11 +30,11 @@ public class NewMediaWithRaceSelectionDialog extends NewMediaDialog {
private AssignRacesToMediaDialog racesForMediaDialog;
public NewMediaWithRaceSelectionDialog(TimePoint defaultStartTime, StringMessages stringMessages,
public NewMediaWithRaceSelectionDialog(MediaServiceAsync mediaService,TimePoint defaultStartTime, StringMessages stringMessages,
SailingServiceAsync sailingService, ErrorReporter errorReporter, RegattaRefresher regattaRefresher,
Set<RegattasDisplayer> regattasDisplayers,
com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback<MediaTrack> dialogCallback) {
super(defaultStartTime, stringMessages, null, dialogCallback);
super(mediaService, defaultStartTime, stringMessages, null, dialogCallback);
this.sailingService = sailingService;
this.errorReporter = errorReporter;
this.regattaRefresher = regattaRefresher;
@@ -1,13 +1,32 @@
package com.sap.sailing.gwt.ui.server;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.channels.Channels;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.mp4parser.IsoFile;
import org.mp4parser.boxes.UserBox;
import org.mp4parser.boxes.iso14496.part12.MovieBox;
import org.mp4parser.boxes.iso14496.part12.MovieHeaderBox;
import org.mp4parser.tools.Path;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.dto.VideoMetadataDTO;
import com.sap.sailing.domain.common.media.MediaTrack;
import com.sap.sailing.gwt.ui.client.MediaService;
import com.sap.sailing.server.RacingEventService;
@@ -88,5 +107,44 @@ public class MediaServiceImpl extends RemoteServiceServlet implements MediaServi
racingEventService().mediaTrackAssignedRacesChanged(mediaTrack);
}
@Override
public VideoMetadataDTO checkMetadata(String url)
throws IOException, ParserConfigurationException, SAXException {
URL input = new URL(url);
boolean canDownload = false;
boolean spherical = false;
Date recordStartedTimer = null;
try(IsoFile isof = new IsoFile(Channels.newChannel(input.openStream()))){
canDownload = true;
// MovieHeaderBox movieHeaderBox = Path.getPath(isof, "moov[0]/mvhd");
// System.out.println(movieHeaderBox.getCreationTime());
UserBox uuidBox = Path.getPath(isof, "moov[0]/trak[0]/uuid");
MovieBox mbox = isof.getMovieBox();
if(mbox != null){
MovieHeaderBox mhb = mbox.getMovieHeaderBox();
if(mhb != null){
recordStartedTimer = mhb.getCreationTime();
}
}
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(new ByteArrayInputStream(uuidBox.getData()));
NodeList childs = doc.getDocumentElement().getChildNodes();
for (int i = 0; i < childs.getLength(); i++) {
Node child = childs.item(i);
if (child.getNodeName().toLowerCase().contains(":spherical")) {
spherical = true;
}
}
}
return new VideoMetadataDTO(canDownload, spherical, recordStartedTimer);
}
}
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry exported="true" kind="lib" path="lib/isoparser-1.9.31.jar"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="output" path="bin"/>
</classpath>
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.mp4parser.isoparser</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
@@ -0,0 +1,13 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Selenium
Bundle-SymbolicName: org.mp4parser.isoparser
Bundle-Version: 1.9.31
Bundle-ClassPath: lib/isoparser-1.9.31.jar
Bundle-Vendor: Sebastian Annies
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Export-Package: org.mp4parser,
org.mp4parser.boxes,
org.mp4parser.boxes.iso14496.part12,
org.mp4parser.tools
@@ -0,0 +1,2 @@
bin.includes = META-INF/,\
lib/isoparser-1.9.31.jar
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>root</artifactId>
<groupId>com.sap.sailing</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.mp4parser.isoparser</artifactId>
<version>1.9.31</version>
<packaging>eclipse-plugin</packaging>
</project>