Replace movies with commandline

This commit is contained in:
Jindra Petřík
2022-12-26 00:30:14 +01:00
parent 9f971a0c88
commit f2646f164b
3 changed files with 325 additions and 353 deletions

View File

@@ -26,9 +26,13 @@ import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
import com.jpexs.decompiler.flash.exporters.modes.MovieExportMode;
import com.jpexs.decompiler.flash.flv.FLVInputStream;
import com.jpexs.decompiler.flash.flv.FLVTAG;
import com.jpexs.decompiler.flash.flv.VIDEODATA;
import com.jpexs.decompiler.flash.tags.base.BoundedTag;
import com.jpexs.decompiler.flash.tags.base.ButtonTag;
import com.jpexs.decompiler.flash.tags.base.DrawableTag;
import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag;
import com.jpexs.decompiler.flash.tags.base.RenderContext;
import com.jpexs.decompiler.flash.timeline.Timeline;
import com.jpexs.decompiler.flash.timeline.Timelined;
@@ -45,6 +49,7 @@ import com.jpexs.decompiler.flash.types.annotations.SWFVersion;
import com.jpexs.decompiler.flash.types.filters.BlendComposite;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Reference;
import com.jpexs.helpers.SerializableImage;
import com.jpexs.video.FrameListener;
import com.jpexs.video.SimpleMediaPlayer;
@@ -57,9 +62,15 @@ import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -94,8 +105,8 @@ public class DefineVideoStreamTag extends DrawableTag implements BoundedTag, Tim
@Reserved
@SWFType(value = BasicType.UB, count = 4)
public int reserved;
@SWFType(value = BasicType.UB, count = 3)
@SWFType(value = BasicType.UB, count = 3)
@EnumValue(value = DefineVideoStreamTag.DEBLOCKING_USE_VIDEOPACKET_VALUE, text = "use VIDEOPACKET value")
@EnumValue(value = DefineVideoStreamTag.DEBLOCKING_OFF, text = "off")
@EnumValue(value = DefineVideoStreamTag.DEBLOCKING_LEVEL1, text = "Level 1 (Fast deblocking filter)")
@@ -108,7 +119,7 @@ public class DefineVideoStreamTag extends DrawableTag implements BoundedTag, Tim
public boolean videoFlagsSmoothing;
@SWFType(BasicType.UI8)
@SWFType(BasicType.UI8)
@EnumValue(value = DefineVideoStreamTag.CODEC_JPEG, text = "JPEG (unused)")
@EnumValue(value = DefineVideoStreamTag.CODEC_SORENSON_H263, text = "Sorenson H.263")
@EnumValue(value = DefineVideoStreamTag.CODEC_SCREEN_VIDEO, text = "Screen video")
@@ -134,24 +145,24 @@ public class DefineVideoStreamTag extends DrawableTag implements BoundedTag, Tim
@Internal
private Timeline timeline;
@Internal
private Map<Integer, VideoFrameTag> frames;
private static final List<SimpleMediaPlayer> players = new ArrayList<>();
private static List<File> tempFiles = new ArrayList<>();
@Internal
private boolean renderingPaused = false;
public static final int CODEC_JPEG = 1;
public static final int CODEC_JPEG = 1;
public static final int CODEC_SORENSON_H263 = 2;
public static final int CODEC_SCREEN_VIDEO = 3;
public static final int CODEC_VP6 = 4;
public static final int CODEC_VP6_ALPHA = 5;
public static final int CODEC_SCREEN_VIDEO_V2 = 6;
public static final int CODEC_AVC = 7; //Is this FLV only, or SWF too?
/**
* use VIDEOPACKET value
*/
@@ -178,24 +189,22 @@ public class DefineVideoStreamTag extends DrawableTag implements BoundedTag, Tim
public static final int DEBLOCKING_LEVEL4 = 5;
public static final int DEBLOCKING_RESERVED1 = 6;
public static final int DEBLOCKING_RESERVED2 = 7;
static {
Runtime.getRuntime().addShutdownHook(new Thread(){
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
for (SimpleMediaPlayer p:players) {
p.stop();
}
for (File f:tempFiles) {
f.delete();
}
for (SimpleMediaPlayer p : players) {
p.stop();
}
for (File f : tempFiles) {
f.delete();
}
}
});
}
/**
* Constructor
*
@@ -276,7 +285,7 @@ public class DefineVideoStreamTag extends DrawableTag implements BoundedTag, Tim
public static boolean displayAvailable() {
return SimpleMediaPlayer.isAvailable();
}
private void initPlayer() {
if (mediaPlayer != null) { // && !mediaPlayer.isFinished()) {
return;
@@ -319,20 +328,22 @@ public class DefineVideoStreamTag extends DrawableTag implements BoundedTag, Tim
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) {
return transformation.toTransform().createTransformedShape(new Rectangle2D.Double(0, 0, width * SWF.unitDivisor, height * SWF.unitDivisor));
}
public synchronized void resetPlayer() {
mediaPlayer.stop();
mediaPlayer = null;
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer = null;
}
}
public synchronized void setPauseRendering(boolean value) {
this.renderingPaused = value;
activeFrame = null;
}
@Override
public synchronized void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, SerializableImage fullImage, boolean isClip, Matrix transformation, Matrix prevTransformation, Matrix absoluteTransformation, Matrix fullTransformation, ColorTransform colorTransform, double unzoom, boolean sameImage, ExportRectangle viewRect, boolean scaleStrokes, int drawMode, int blendMode) {
if (renderingPaused || !SimpleMediaPlayer.isAvailable()) {
Graphics2D g = (Graphics2D) image.getBufferedImage().getGraphics();
Matrix mat = transformation;
@@ -354,28 +365,27 @@ public class DefineVideoStreamTag extends DrawableTag implements BoundedTag, Tim
if (ratio == -1) {
ratio = 0;
}
Set<Integer> keyFrames = getFrames().keySet();
int f = 0;
for(int i = 0; i <= ratio; i++) {
for (int i = 0; i <= ratio; i++) {
if (keyFrames.contains(i)) {
f = i;
}
}
synchronized (DefineVideoStreamTag.class) {
if (!(activeFrame != null && lastFrame == f))
{
if (!(activeFrame != null && lastFrame == f)) {
initPlayer();
float oneFr = 0; //1f / (getNumFrames() + 2);
float oneFr = 0; //1f / (getNumFrames() + 2);
synchronized (getFrameLock) {
activeFrame = null;
}
mediaPlayer.setPosition(((float) f) / (getNumFrames() + 2) - (f == 0 ? 0 : oneFr / 10f));
}
mediaPlayer.setPosition(((float) f) / (getNumFrames() + 2) - (f == 0 ? 0 : oneFr / 10f));
try {
synchronized (getFrameLock) {
if (activeFrame == null) {
@@ -435,7 +445,7 @@ public class DefineVideoStreamTag extends DrawableTag implements BoundedTag, Tim
initTimeline();
return timeline;
}
private Map<Integer, VideoFrameTag> getFrames() {
if (this.frames != null) {
return this.frames;
@@ -523,4 +533,261 @@ public class DefineVideoStreamTag extends DrawableTag implements BoundedTag, Tim
public int getFrameCount() {
return numFrames;
}
public void replace(InputStream fis) throws IOException {
List<FLVTAG> videoTags = new ArrayList<>();
FLVInputStream flvIs = new FLVInputStream(fis);
Reference<Boolean> audioPresent = new Reference<>(false);
Reference<Boolean> videoPresent = new Reference<>(false);
flvIs.readHeader(audioPresent, videoPresent);
List<FLVTAG> flvTags = flvIs.readTags();
for (FLVTAG tag : flvTags) {
if (tag.tagType == FLVTAG.DATATYPE_VIDEO) {
videoTags.add(tag);
}
}
if (!videoTags.isEmpty()) {
FLVTAG firstVideoTag = videoTags.get(0);
VIDEODATA videoData = ((VIDEODATA) firstVideoTag.data);
FLVInputStream dis = new FLVInputStream(new ByteArrayInputStream(videoData.videoData));
int newWidth = 0;
int newHeight = 0;
switch (videoData.codecId) {
case VIDEODATA.CODEC_SORENSON_H263:
dis.readUB(17); //pictureStartCode
dis.readUB(5); //version
dis.readUB(8); //temporalReference
int pictureSize = (int) dis.readUB(3);
switch (pictureSize) {
case 0:
newWidth = (int) dis.readUB(8);
newHeight = (int) dis.readUB(8);
break;
case 1:
newWidth = (int) dis.readUB(16);
newHeight = (int) dis.readUB(16);
break;
case 2:
newWidth = 352;
newHeight = 288;
break;
case 3:
newWidth = 176;
newHeight = 144;
break;
case 4:
newWidth = 128;
newHeight = 96;
break;
case 5:
newWidth = 320;
newHeight = 240;
break;
case 6:
newWidth = 160;
newHeight = 120;
break;
}
break;
case VIDEODATA.CODEC_SCREEN_VIDEO:
case VIDEODATA.CODEC_SCREEN_VIDEO_V2:
dis.readUB(4); //blockWidth
newWidth = (int) dis.readUB(12);
dis.readUB(4); //blockHeight
newHeight = (int) dis.readUB(12);
break;
case VIDEODATA.CODEC_VP6:
case VIDEODATA.CODEC_VP6_ALPHA:
int horizontalAdjustment = (int) dis.readUB(4);
int verticalAdjustment = (int) dis.readUB(4);
if (videoData.codecId == VIDEODATA.CODEC_VP6_ALPHA) {
dis.readUI24(); //offsetToAlpha
}
int frameMode = (int) dis.readUB(1); //frameMode
dis.readUB(6); //qp
int multiStream = (int) dis.readUB(1); //marker
if (frameMode == 0) {
int vp3VersionNo = (int) dis.readUB(5);
int vpProfile = (int) dis.readUB(2);
dis.readUB(1); //interlace
if (multiStream == 1 || vpProfile == 0) {
dis.readUI16(); //offset
}
int dim_y = dis.readUI8();
int dim_x = dis.readUI8();
//dis.readUI8(); //render_y
//dis.readUI8(); //render_x
newWidth = 16 * dim_x - horizontalAdjustment;
newHeight = 16 * dim_y - verticalAdjustment;
}
break;
case VIDEODATA.CODEC_AVC:
throw new IOException("AVC codec is not supported for import");
case VIDEODATA.CODEC_JPEG:
throw new IOException("JPEG codec is not supported for import");
}
if (newWidth <= 0 || newHeight <= 0) {
throw new IOException("Invalid dimension");
}
this.codecID = videoData.codecId;
this.width = newWidth;
this.height = newHeight;
this.videoFlagsDeblocking = DefineVideoStreamTag.DEBLOCKING_OFF;
this.videoFlagsSmoothing = true;
this.numFrames = videoTags.size();
setPauseRendering(true);
videoTags.sort(new Comparator<FLVTAG>() {
@Override
public int compare(FLVTAG o1, FLVTAG o2) {
return Long.compare(o1.timeStamp, o2.tagType);
}
});
//remove old VideoFrame tags
Map<Integer, VideoFrameTag> frames = new HashMap<>();
SWF.populateVideoFrames(characterID, swf.getTags(), frames);
Timelined timelined = null;
for (VideoFrameTag t : frames.values()) {
t.getTimelined().removeTag(t);
timelined = t.getTimelined();
}
timelined.resetTimeline();
int startFrame = 0;
int placeDepth = -1;
for (Tag t : timelined.getTags()) {
if (t instanceof ShowFrameTag) {
startFrame++;
}
if (t instanceof PlaceObjectTypeTag) {
PlaceObjectTypeTag pt = (PlaceObjectTypeTag) t;
if (pt.getCharacterId() == characterID) {
placeDepth = pt.getDepth();
break;
}
}
}
int importLastFrame = -1;
if (timelined != null) {
VideoFrameTag lastVideoFrame = null;
for (FLVTAG ftag : videoTags) {
videoData = ((VIDEODATA) ftag.data);
if (videoData.codecId == VIDEODATA.CODEC_VP6 || videoData.codecId == VIDEODATA.CODEC_VP6_ALPHA) {
int horizontalAdjustment = (int) dis.readUB(4);
int verticalAdjustment = (int) dis.readUB(4);
if (videoData.codecId == VIDEODATA.CODEC_VP6_ALPHA) {
dis.readUI24(); //offsetToAlpha
}
int frameMode = (int) dis.readUB(1); //frameMode
dis.readUB(6); //qp
int multiStream = (int) dis.readUB(1); //marker
if (frameMode == 1) {
if (multiStream == 1) { // || version2 == 0) { ???
dis.readUI16(); //buff2Offset
}
dis.readUB(1); //refreshGoldenFrame
int useLoopFilter = (int) dis.readUB(1); //useLoopFilter
if (useLoopFilter == 1) {
int loopFilterSelector = (int) dis.readUB(1);
if (loopFilterSelector == 0) {
videoFlagsDeblocking = DefineVideoStreamTag.DEBLOCKING_LEVEL1;
} else if (loopFilterSelector == 1) {
videoFlagsDeblocking = DefineVideoStreamTag.DEBLOCKING_LEVEL4;
}
}
}
}
int idealFrame = startFrame + (int) Math.floor(swf.frameRate * ftag.timeStamp / 1000.0);
if (idealFrame == importLastFrame) {
idealFrame++;
}
int swfFrameNum = -1;
ReadOnlyTagList tagList = timelined.getTags();
int p = 0;
boolean found = false;
for (; p < tagList.size(); p++) {
Tag t = tagList.get(p);
if (t instanceof ShowFrameTag) {
swfFrameNum++;
if (swfFrameNum == idealFrame) {
found = true;
break;
}
}
}
//add frames when necessary
if (!found) {
p--;
swfFrameNum++;
for (; swfFrameNum <= idealFrame; swfFrameNum++) {
PlaceObject2Tag placeObject = new PlaceObject2Tag(swf);
placeObject.setTimelined(timelined);
placeObject.depth = placeDepth;
placeObject.placeFlagMove = true;
placeObject.placeFlagHasRatio = true;
placeObject.ratio = swfFrameNum - startFrame;
timelined.addTag(placeObject);
p++;
ShowFrameTag sft = new ShowFrameTag(swf);
sft.setTimelined(timelined);
timelined.addTag(sft);
p++;
}
swfFrameNum--;
}
VideoFrameTag videoFrameTag = new VideoFrameTag(swf);
videoFrameTag.streamID = characterID;
videoFrameTag.frameNum = swfFrameNum - startFrame;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
VIDEODATA vdata = (VIDEODATA) ftag.data;
switch (codecID) {
case DefineVideoStreamTag.CODEC_VP6:
case DefineVideoStreamTag.CODEC_VP6_ALPHA:
baos.write(Arrays.copyOfRange(vdata.videoData, 1 /*strip adjustment*/, vdata.videoData.length));
break;
default:
baos.write(vdata.videoData);
break;
}
videoFrameTag.videoData = new ByteArrayRange(baos.toByteArray());
timelined.addTag(p, videoFrameTag);
videoFrameTag.setTimelined(timelined);
importLastFrame = idealFrame;
}
}
List<Tag> tagsToRemove = new ArrayList<>();
for (Tag t : timelined.getTags()) {
if (t instanceof PlaceObjectTypeTag) {
PlaceObjectTypeTag pt = (PlaceObjectTypeTag) t;
if (pt.getDepth() == placeDepth) {
int ratio = pt.getRatio();
if (ratio > importLastFrame - startFrame) {
tagsToRemove.add(t);
}
}
}
}
for (Tag t : tagsToRemove) {
timelined.removeTag(t);
}
setModified(true);
timelined.resetTimeline();
resetTimeline();
resetPlayer();
setPauseRendering(false);
}
}
}