使用 Node.js 管理簡報中的影片框架

簡介

影片可以協助說明概念並吸引觀眾。Aspose.Slides for Node.js via Java 讓您能在投影片中加入影片框架、調整播放設定、管理字幕,並擷取嵌入的影片資料。

PowerPoint 支援本機影片以及連結至線上影片(例如 YouTube 影片)。

為了表示影片資料與影片框架,Aspose.Slides 提供了 Video 類別、VideoFrame 類別以及其他相關型別。

建立嵌入式影片框架

如果要加入至投影片的影片檔案儲存在本機,您可以建立影片框架將影片嵌入簡報中。

此範例將本機影片嵌入現有簡報的第一張投影片,並儲存結果。框架的座標與尺寸以點為單位。因為 LoadingStreamBehavior.KeepLocked 在簡報使用時保持鎖定,資料流會在儲存完成前保持開啟。

const aspose = { slides: require("aspose.slides.via.java") };
const java = require("java");

const presentation = new aspose.slides.Presentation("presentation.pptx");
try {
    const videoStream = java.newInstanceSync("java.io.FileInputStream", "video.mp4");
    try {
        const slide = presentation.getSlides().get_Item(0);

        const video = presentation.getVideos().addVideo(videoStream, aspose.slides.LoadingStreamBehavior.KeepLocked);
        slide.getShapes().addVideoFrame(10, 10, 150, 250, video);

        presentation.save("embedded_video.pptx", aspose.slides.SaveFormat.Pptx);
    } finally {
        videoStream.close();
    }
} finally {
    presentation.dispose();
}

您也可以直接將本機影片路徑傳遞給 addVideoFrame。此範例將影片嵌入新簡報的第一張投影片。影片必須在簡報儲存之前保持可存取。

const aspose = { slides: require("aspose.slides.via.java") };

const presentation = new aspose.slides.Presentation();
try {
    const slide = presentation.getSlides().get_Item(0);

    slide.getShapes().addVideoFrame(50, 150, 300, 150, "video.avi");

    presentation.save("video_from_path.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

建立來自網路來源的影片框架

Microsoft PowerPoint 支援在簡報中使用線上影片。您可以建立連結至線上影片(例如 YouTube 影片)的影片框架。

此範例在第一張投影片加入 YouTube 影片連結與縮圖。將影片識別碼替換為其他影片即可使用。setPlayMode 方法請求自動播放。下載縮圖與播放影片均需網際網路連線。簡報檢視器亦必須支援線上影片播放。

const aspose = { slides: require("aspose.slides.via.java") };
const java = require("java");

const presentation = new aspose.slides.Presentation();
try {
    const slide = presentation.getSlides().get_Item(0);

    const videoId = "aqz-KE-bpKQ";
    const videoUrl = "https://www.youtube.com/embed/" + videoId;
    const videoFrame = slide.getShapes().addVideoFrame(10, 10, 427, 240, videoUrl);
    videoFrame.setPlayMode(aspose.slides.VideoPlayModePreset.Auto);

    const thumbnailUrl = "https://img.youtube.com/vi/" + videoId + "/hqdefault.jpg";
    const thumbnailLocation = java.newInstanceSync("java.net.URL", thumbnailUrl);
    const thumbnailStream = thumbnailLocation.openStream();
    try {
        const thumbnail = presentation.getImages().addImage(thumbnailStream);
        videoFrame.getPictureFormat().getPicture().setImage(thumbnail);
    } finally {
        thumbnailStream.close();
    }

    presentation.save("online_video.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

全螢幕模式播放影片

在培訓簡報中,您可以全螢幕播放軟體示範,讓觀眾看到細節。將 true 傳入 setFullScreenMode 即可在播放期間啟用此行為。

此範例開啟簡報,於第一張投影片找到第一個 VideoFrame,並啟用全螢幕播放。輸入的簡報必須至少在第一張投影片上含有一個現有的影片框架。

const aspose = { slides: require("aspose.slides.via.java") };
const java = require("java");

const presentation = new aspose.slides.Presentation("training.pptx");
try {
    const slide = presentation.getSlides().get_Item(0);

    for (let shapeIndex = 0; shapeIndex < slide.getShapes().size(); shapeIndex++) {
        const shape = slide.getShapes().get_Item(shapeIndex);
        if (java.instanceOf(shape, "com.aspose.slides.VideoFrame")) {
            const videoFrame = shape;
            videoFrame.setFullScreenMode(true);
            break;
        }
    }

    presentation.save("full_screen_video.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

全螢幕播放控制影片的顯示方式。除此之外,setPlayMode 控制是否自動或點擊開始播放,且 setPlayLoopMode 控制是否重複播放。若要選擇開始行為,將播放模式設為 VideoPlayModePreset.Auto or VideoPlayModePreset.OnClick。此範例保留了既有的開始與迴圈設定。

播放後倒帶影片

在培訓簡報中,將示範影片倒回開頭可讓簡報者再次播放。將 true 傳入 setRewindVideo 即可在播放結束後將影片倒回開頭。

此範例開啟簡報,於第一張投影片找到第一個 VideoFrame,並啟用倒帶。它會停用迴圈,使播放能結束,並設定點擊開始播放。輸入的簡報必須至少在第一張投影片上含有一個現有的影片框架。

const aspose = { slides: require("aspose.slides.via.java") };
const java = require("java");

const presentation = new aspose.slides.Presentation("training.pptx");
try {
    const slide = presentation.getSlides().get_Item(0);

    for (let shapeIndex = 0; shapeIndex < slide.getShapes().size(); shapeIndex++) {
        const shape = slide.getShapes().get_Item(shapeIndex);
        if (java.instanceOf(shape, "com.aspose.slides.VideoFrame")) {
            const videoFrame = shape;
            videoFrame.setRewindVideo(true);
            videoFrame.setPlayLoopMode(false);
            videoFrame.setPlayMode(aspose.slides.VideoPlayModePreset.OnClick);
            break;
        }
    }

    presentation.save("rewind_video.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

倒帶會把影片返回開頭而不會再次自動開始。相較之下,將 setPlayLoopMode 設為 true 會自動重複播放。若希望影片播放完畢後保持可重新播放的狀態,請停用迴圈。setPlayMode 獨立控制自動或點擊啟動;此範例使用 VideoPlayModePreset.OnClick 讓簡報者自行決定何時開始播放。請先設定迴圈,再設定播放模式,如範例所示。倒帶與 setFullScreenMode 的設定互不影響。

裁剪影片框架

使用 VideoFrame.setTrimFromStart 與 VideoFrame.setTrimFromEnd 可在播放時跳過影片開頭或結尾的部分。兩個值皆以毫秒為單位。裁剪僅會變更播放設定,不會改變嵌入的影片資料。

設定裁剪參數

此範例嵌入本機影片,於播放時跳過前 2.5 秒與最後 1 秒。請使用長度超過 3.5 秒的影片,以確保仍留下可播放的片段。

const aspose = { slides: require("aspose.slides.via.java") };
const java = require("java");
const fs = require("fs");

const presentation = new aspose.slides.Presentation();
try {
    const slide = presentation.getSlides().get_Item(0);

    const videoBuffer = fs.readFileSync("video.mp4");
    const videoData = java.newArray("byte", Array.from(videoBuffer));
    const video = presentation.getVideos().addVideo(videoData);

    const videoFrame = slide.getShapes().addVideoFrame(50, 50, 640, 360, video);
    videoFrame.setTrimFromStart(2500);
    videoFrame.setTrimFromEnd(1000);

    presentation.save("video_with_trim.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

讀取裁剪參數

此範例列印第一張投影片上第一個影片框架的裁剪值(毫秒)。簡報必須至少包含一張投影片;若該投影片沒有影片框架,則不會列印任何內容。前一個範例的輸出值為 2500 與 1000。

const aspose = { slides: require("aspose.slides.via.java") };
const java = require("java");

const presentation = new aspose.slides.Presentation("video_with_trim.pptx");
try {
    const slide = presentation.getSlides().get_Item(0);

    for (let shapeIndex = 0; shapeIndex < slide.getShapes().size(); shapeIndex++) {
        const shape = slide.getShapes().get_Item(shapeIndex);
        if (java.instanceOf(shape, "com.aspose.slides.VideoFrame")) {
            const videoFrame = shape;
            console.log("Trim from start: " + videoFrame.getTrimFromStart() + " ms");
            console.log("Trim from end: " + videoFrame.getTrimFromEnd() + " ms");
            break;
        }
    }
} finally {
    presentation.dispose();
}

管理影片字幕

Aspose.Slides 允許您在 PowerPoint 簡報的影片框架中管理關閉式字幕。字幕以 WebVTT 格式儲存,並可透過 VideoFrame.getCaptionTracks 方法取得。

為影片框架新增字幕

此範例嵌入本機影片,並加入一條標示為 English 的 WebVTT 字幕軌道。字幕的時間戳記應與影片相符。儲存的簡報會同時包含影片與其字幕。

const aspose = { slides: require("aspose.slides.via.java") };
const java = require("java");
const fs = require("fs");

const presentation = new aspose.slides.Presentation();
try {
    const slide = presentation.getSlides().get_Item(0);

    const videoBuffer = fs.readFileSync("video.mp4");
    const videoData = java.newArray("byte", Array.from(videoBuffer));
    const video = presentation.getVideos().addVideo(videoData);

    const videoFrame = slide.getShapes().addVideoFrame(0, 0, 100, 100, video);
    videoFrame.getCaptionTracks().add("English", "track.vtt");

    presentation.save("video_with_captions.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

CaptionsCollection 類別亦提供 addFromStream 方法,以從資料流新增字幕。

從影片框架擷取字幕

此範例將第一張投影片上所有影片框架的字幕軌道另存為個別的 WebVTT 檔案。使用連續編號以保持輸出檔案的唯一性。主控台會報告擷取的軌道數量。簡報必須至少包含一張投影片。

const aspose = { slides: require("aspose.slides.via.java") };
const java = require("java");
const fs = require("fs");

const presentation = new aspose.slides.Presentation("video_with_captions.pptx");
try {
    const slide = presentation.getSlides().get_Item(0);

    let trackCount = 0;
    for (let shapeIndex = 0; shapeIndex < slide.getShapes().size(); shapeIndex++) {
        const shape = slide.getShapes().get_Item(shapeIndex);
        if (java.instanceOf(shape, "com.aspose.slides.VideoFrame")) {
            const videoFrame = shape;
            for (let trackIndex = 0; trackIndex < videoFrame.getCaptionTracks().getCount(); trackIndex++) {
                const captionTrack = videoFrame.getCaptionTracks().get_Item(trackIndex);
                trackCount++;
                const outputPath = "captions_" + trackCount + ".vtt";
                const outputData = Buffer.from(captionTrack.getBinaryData());
                fs.writeFileSync(outputPath, outputData);
            }
        }
    }

    console.log("Caption tracks extracted: " + trackCount);
} finally {
    presentation.dispose();
}

每個 Captions 物件會公開字幕識別碼、標籤、二進位資料以及以 UTF-8 字串呈現的字幕文字。

從影片框架移除字幕

此範例移除第一張投影片上第一個形狀位置的影片框架的所有字幕,並儲存結果。它假設投影片與形狀均已存在,且該形狀為影片框架。

const aspose = { slides: require("aspose.slides.via.java") };

const presentation = new aspose.slides.Presentation("video_with_captions.pptx");
try {
    const slide = presentation.getSlides().get_Item(0);

    const videoFrame = slide.getShapes().get_Item(0);
    videoFrame.getCaptionTracks().clear();

    presentation.save("video_without_captions.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

如果只需要移除單一字幕軌道,請使用 remove 或 removeAt 方法取代 clear。

從投影片擷取影片

除了將影片加入投影片之外,Aspose.Slides 亦允許您擷取簡報中嵌入的影片。

此範例將每張投影片的嵌入影片擷取為單獨的編號二進位檔案。連結影片會被略過,因為它們不含嵌入資料。主控台會列印每支影片的 MIME 類型與總計數量。輸出使用通用的 .bin 副檔名;如有需要,可依報告的媒體類型自行更改副檔名。

const aspose = { slides: require("aspose.slides.via.java") };
const java = require("java");
const fs = require("fs");

const presentation = new aspose.slides.Presentation("presentation_with_videos.pptx");
try {
    let videoCount = 0;
    for (let slideIndex = 0; slideIndex < presentation.getSlides().size(); slideIndex++) {
        const slide = presentation.getSlides().get_Item(slideIndex);
        for (let shapeIndex = 0; shapeIndex < slide.getShapes().size(); shapeIndex++) {
            const shape = slide.getShapes().get_Item(shapeIndex);
            if (java.instanceOf(shape, "com.aspose.slides.VideoFrame")) {
                const videoFrame = shape;
                const video = videoFrame.getEmbeddedVideo();
                if (video == null) {
                    console.log("Skipped a linked video: no embedded data is available.");
                    continue;
                }

                videoCount++;
                const outputPath = "extracted_video_" + videoCount + ".bin";
                const outputData = Buffer.from(video.getBinaryData());
                fs.writeFileSync(outputPath, outputData);
                console.log("Video " + videoCount + ": " + video.getContentType());
            }
        }
    }

    console.log("Embedded videos extracted: " + videoCount);
} finally {
    presentation.dispose();
}

常見問題

可以變更影片框架的哪些播放參數?

您可以透過 VideoFrame 物件的方法控制 playback mode(自動或點擊)以及 looping。

加入影片會影響 PPTX 檔案大小嗎?

會的。若嵌入本機影片,二進位資料會併入文件,簡報大小會隨影片檔案大小成比例增加。若連結至線上影片並加入縮圖,簡報只儲存連結與預覽圖像,大小增幅通常較小。

我可以在不變更位置和尺寸的情況下,更換現有影片框架中的影片嗎?

可以。您可以在保持形狀幾何的前提下,使用 setEmbeddedVideo 交換框架內的影片內容,這是更新現有版面媒體的常見情境。

可以判斷嵌入影片的內容類型(MIME)嗎?

可以。嵌入影片具有可透過 getContentType 讀取的內容類型,您可將其用於例如儲存至磁碟等用途。