Search in sources :

Example 1 with RenderFrameData

use of com.helospark.tactview.core.timeline.framemerge.RenderFrameData in project tactview by helospark.

the class TimelineManagerRenderService method getFrame.

public TimelineRenderResult getFrame(TimelineManagerFramesRequest request) {
    List<TimelineClip> allClips = timelineManager.channels.stream().map(channel -> channel.getDataAt(request.getPosition())).flatMap(Optional::stream).collect(Collectors.toList());
    Map<String, TimelineClip> clipsToRender = allClips.stream().collect(Collectors.toMap(a -> a.getId(), a -> a));
    List<String> renderOrder = allClips.stream().filter(a -> a.isEnabled(request.getPosition())).map(a -> a.getId()).collect(Collectors.toList());
    List<TreeNode> tree = buildRenderTree(clipsToRender, request.getPosition());
    List<List<TimelineClip>> layers = new ArrayList<>();
    recursiveLayering(tree, 0, layers);
    Map<String, RenderFrameData> clipsToFrames = new ConcurrentHashMap<>();
    Map<String, AudioFrameResult> audioToFrames = new ConcurrentHashMap<>();
    Map<String, RegularRectangle> clipToExpandedPosition = new ConcurrentHashMap<>();
    for (int i = 0; i < layers.size(); ++i) {
        List<CompletableFuture<Void>> futures = new ArrayList<>();
        for (var clip : layers.get(i)) {
            if (clip instanceof VisualTimelineClip && request.isNeedVideo()) {
                // TODO: rest later
                VisualTimelineClip visualClip = (VisualTimelineClip) clip;
                futures.add(CompletableFuture.supplyAsync(() -> {
                    Map<String, ReadOnlyClipImage> requiredClips = visualClip.getClipDependency(request.getPosition()).stream().filter(a -> clipsToFrames.containsKey(a)).map(a -> clipsToFrames.get(a)).collect(Collectors.toMap(a -> a.id, a -> a.clipFrameResult));
                    Map<String, ReadOnlyClipImage> channelCopiedClips = visualClip.getChannelDependency(request.getPosition()).stream().flatMap(channelId -> timelineManagerAccessor.findChannelWithId(channelId).stream()).flatMap(channel -> channel.getDataAt(request.getPosition()).stream()).filter(a -> clipsToFrames.containsKey(a.getId())).map(a -> clipsToFrames.get(a.getId())).collect(Collectors.toMap(a -> a.channelId, a -> a.clipFrameResult, (a, b) -> a, HashMap::new));
                    ReadOnlyClipImage adjustmentImage = null;
                    if (clip instanceof AdjustmentLayerProceduralClip) {
                        Map<String, RenderFrameData> framesBelow = new TreeMap<>();
                        int startChannel = timelineManagerAccessor.findChannelIndexForClipId(visualClip.getId()).get() + 1;
                        for (int k = startChannel; k < timelineManager.channels.size(); ++k) {
                            Optional<TimelineClip> clipAtChannel = timelineManager.channels.get(k).getDataAt(request.getPosition());
                            if (clipAtChannel.isPresent()) {
                                String clipId = clipAtChannel.get().getId();
                                framesBelow.put(clipId, clipsToFrames.get(clipId));
                            }
                        }
                        adjustmentImage = renderBelowLayers(request, renderOrder, framesBelow);
                        channelCopiedClips.put(AdjustmentLayerProceduralClip.LAYER_ID, adjustmentImage);
                    }
                    GetFrameRequest frameRequest = GetFrameRequest.builder().withScale(request.getScale()).withPosition(request.getPosition()).withExpectedWidth(request.getPreviewWidth()).withExpectedHeight(request.getPreviewHeight()).withApplyEffects(request.isEffectsEnabled()).withRequestedClips(requiredClips).withRequestedChannelClips(channelCopiedClips).withLowResolutionPreview(request.isLowResolutionPreview()).build();
                    ReadOnlyClipImage frameResult = visualClip.getFrame(frameRequest);
                    ReadOnlyClipImage expandedFrame = expandFrame(request, visualClip, frameResult, clipToExpandedPosition);
                    BlendModeStrategy blendMode = visualClip.getBlendModeAt(request.getPosition());
                    double alpha = visualClip.getAlpha(request.getPosition());
                    GlobalMemoryManagerAccessor.memoryManager.returnBuffer(frameResult.getBuffer());
                    if (adjustmentImage != null) {
                        GlobalMemoryManagerAccessor.memoryManager.returnBuffer(adjustmentImage.getBuffer());
                    }
                    String channelId = timelineManagerAccessor.findChannelForClipId(visualClip.getId()).get().getId();
                    return new RenderFrameData(visualClip.getId(), alpha, blendMode, expandedFrame, clip.getEffectsAtGlobalPosition(request.getPosition(), AbstractVideoTransitionEffect.class), channelId);
                }, executorService).thenAccept(a -> {
                    clipsToFrames.put(visualClip.getId(), a);
                }).exceptionally(e -> {
                    logger.error("Unable to render", e);
                    return null;
                }));
            } else if (clip instanceof AudibleTimelineClip && request.isNeedSound()) {
                AudibleTimelineClip audibleClip = (AudibleTimelineClip) clip;
                futures.add(CompletableFuture.supplyAsync(() -> {
                    int sampleRateToUse = request.getAudioSampleRate().orElse(projectRepository.getSampleRate());
                    int bytesPerSampleToUse = request.getAudioBytesPerSample().orElse(projectRepository.getBytesPerSample());
                    int numberOfChannels = request.getNumberOfChannels().orElse(projectRepository.getNumberOfChannels());
                    TimelineLength defaultLength = new TimelineLength(projectRepository.getFrameTime());
                    TimelineLength length = request.getAudioLength().orElse(defaultLength);
                    AudioRequest audioRequest = AudioRequest.builder().withApplyEffects(request.isEffectsEnabled()).withPosition(request.getPosition()).withLength(length).withSampleRate(sampleRateToUse).withBytesPerSample(bytesPerSampleToUse).withNumberOfChannels(numberOfChannels).build();
                    return audibleClip.requestAudioFrame(audioRequest);
                }, executorService).exceptionally(e -> {
                    logger.error("Unable to get audio", e);
                    return null;
                }).thenAccept(a -> {
                    if (a == null) {
                        logger.error("Unable to get audio");
                    } else {
                        audioToFrames.put(audibleClip.getId(), a);
                    }
                }));
            }
        }
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).join();
    }
    ReadOnlyClipImage finalImage = request.isNeedVideo() ? renderVideo(request, renderOrder, clipsToFrames) : null;
    AudioFrameResult audioBuffer = renderAudio(renderOrder, audioToFrames, request);
    clipsToFrames.values().stream().forEach(a -> GlobalMemoryManagerAccessor.memoryManager.returnBuffer(a.clipFrameResult.getBuffer()));
    audioToFrames.values().stream().flatMap(a -> a.getChannels().stream()).forEach(a -> GlobalMemoryManagerAccessor.memoryManager.returnBuffer(a));
    ReadOnlyClipImage finalResult = executeGlobalEffectsOn(finalImage);
    return new TimelineRenderResult(new AudioVideoFragment(finalResult, audioBuffer), new HashMap<>(clipToExpandedPosition));
}
Also used : ThreadFactoryBuilder(com.google.common.util.concurrent.ThreadFactoryBuilder) Component(com.helospark.lightdi.annotation.Component) AdjustmentLayerProceduralClip(com.helospark.tactview.core.timeline.proceduralclip.channelcopy.AdjustmentLayerProceduralClip) BlendModeStrategy(com.helospark.tactview.core.timeline.blendmode.BlendModeStrategy) HashMap(java.util.HashMap) CompletableFuture(java.util.concurrent.CompletableFuture) ReadOnlyClipImage(com.helospark.tactview.core.timeline.image.ReadOnlyClipImage) ByteBuffer(java.nio.ByteBuffer) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) BigDecimal(java.math.BigDecimal) AbstractVideoTransitionEffect(com.helospark.tactview.core.timeline.effect.transition.AbstractVideoTransitionEffect) Map(java.util.Map) ExecutorService(java.util.concurrent.ExecutorService) ClipImage(com.helospark.tactview.core.timeline.image.ClipImage) Logger(org.slf4j.Logger) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Set(java.util.Set) Slf4j(com.helospark.tactview.core.util.logger.Slf4j) FrameBufferMerger(com.helospark.tactview.core.timeline.framemerge.FrameBufferMerger) FrameExtender(com.helospark.tactview.core.timeline.render.FrameExtender) Collectors(java.util.stream.Collectors) RenderFrameData(com.helospark.tactview.core.timeline.framemerge.RenderFrameData) Executors(java.util.concurrent.Executors) RegularRectangle(com.helospark.tactview.core.timeline.TimelineRenderResult.RegularRectangle) List(java.util.List) TreeMap(java.util.TreeMap) GlobalMemoryManagerAccessor(com.helospark.tactview.core.decoder.framecache.GlobalMemoryManagerAccessor) Optional(java.util.Optional) ProjectRepository(com.helospark.tactview.core.repository.ProjectRepository) HashMap(java.util.HashMap) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) ArrayList(java.util.ArrayList) CompletableFuture(java.util.concurrent.CompletableFuture) BlendModeStrategy(com.helospark.tactview.core.timeline.blendmode.BlendModeStrategy) ReadOnlyClipImage(com.helospark.tactview.core.timeline.image.ReadOnlyClipImage) ArrayList(java.util.ArrayList) List(java.util.List) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Optional(java.util.Optional) RegularRectangle(com.helospark.tactview.core.timeline.TimelineRenderResult.RegularRectangle) RenderFrameData(com.helospark.tactview.core.timeline.framemerge.RenderFrameData) HashMap(java.util.HashMap) Map(java.util.Map) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) TreeMap(java.util.TreeMap) AdjustmentLayerProceduralClip(com.helospark.tactview.core.timeline.proceduralclip.channelcopy.AdjustmentLayerProceduralClip)

Aggregations

ThreadFactoryBuilder (com.google.common.util.concurrent.ThreadFactoryBuilder)1 Component (com.helospark.lightdi.annotation.Component)1 GlobalMemoryManagerAccessor (com.helospark.tactview.core.decoder.framecache.GlobalMemoryManagerAccessor)1 ProjectRepository (com.helospark.tactview.core.repository.ProjectRepository)1 RegularRectangle (com.helospark.tactview.core.timeline.TimelineRenderResult.RegularRectangle)1 BlendModeStrategy (com.helospark.tactview.core.timeline.blendmode.BlendModeStrategy)1 AbstractVideoTransitionEffect (com.helospark.tactview.core.timeline.effect.transition.AbstractVideoTransitionEffect)1 FrameBufferMerger (com.helospark.tactview.core.timeline.framemerge.FrameBufferMerger)1 RenderFrameData (com.helospark.tactview.core.timeline.framemerge.RenderFrameData)1 ClipImage (com.helospark.tactview.core.timeline.image.ClipImage)1 ReadOnlyClipImage (com.helospark.tactview.core.timeline.image.ReadOnlyClipImage)1 AdjustmentLayerProceduralClip (com.helospark.tactview.core.timeline.proceduralclip.channelcopy.AdjustmentLayerProceduralClip)1 FrameExtender (com.helospark.tactview.core.timeline.render.FrameExtender)1 Slf4j (com.helospark.tactview.core.util.logger.Slf4j)1 BigDecimal (java.math.BigDecimal)1 ByteBuffer (java.nio.ByteBuffer)1 ArrayList (java.util.ArrayList)1 HashMap (java.util.HashMap)1 HashSet (java.util.HashSet)1 List (java.util.List)1