步驟 2:編寫並檢查代碼 - Amazon Kinesis Video Streams

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

步驟 2:編寫並檢查代碼

本節您將檢驗 Java 程式庫及測試程式碼,並了解如何在自己的程式碼之中,使用程式庫的各項工具。

Kinesis 視訊串流剖析器程式庫包含下列工具:

StreamingMkvReader

此類別能以不封鎖的方式,由串流讀取指定的 MKV 元素。

下列程式碼範例 (來自 FragmentMetadataVisitorTest) 顯示如何建立及使用 Streaming MkvReader,由名為 inputStream 的輸入串流擷取 MkvElement 物件。

StreamingMkvReader mkvStreamReader = StreamingMkvReader.createDefault(new InputStreamParserByteSource(inputStream)); while (mkvStreamReader.mightHaveNext()) { Optional<MkvElement> mkvElement = mkvStreamReader.nextIfAvailable(); if (mkvElement.isPresent()) { mkvElement.get().accept(fragmentVisitor); ... } } }

FragmentMetadataVisitor

此類別會擷取片段 (媒體元素) 的中繼資料,並追蹤包含媒體資訊的個別資料串流,例如編解碼器私人資料、像素寬度或像素高度。

下列程式碼範例 (來自 FragmentMetadataVisitorTest 檔案) 顯示如何使用 FragmentMetadataVisitorMkvElement 物件擷取資料:

FragmentMetadataVisitor fragmentVisitor = FragmentMetadataVisitor.create(); StreamingMkvReader mkvStreamReader = StreamingMkvReader.createDefault(new InputStreamParserByteSource(in)); int segmentCount = 0; while(mkvStreamReader.mightHaveNext()) { Optional<MkvElement> mkvElement = mkvStreamReader.nextIfAvailable(); if (mkvElement.isPresent()) { mkvElement.get().accept(fragmentVisitor); if (MkvTypeInfos.SIMPLEBLOCK.equals(mkvElement.get().getElementMetaData().getTypeInfo())) { MkvDataElement dataElement = (MkvDataElement) mkvElement.get(); Frame frame = ((MkvValue<Frame>)dataElement.getValueCopy()).getVal(); MkvTrackMetadata trackMetadata = fragmentVisitor.getMkvTrackMetadata(frame.getTrackNumber()); assertTrackAndFragmentInfo(fragmentVisitor, frame, trackMetadata); } if (MkvTypeInfos.SEGMENT.equals(mkvElement.get().getElementMetaData().getTypeInfo())) { if (mkvElement.get() instanceof MkvEndMasterElement) { if (segmentCount < continuationTokens.size()) { Optional<String> continuationToken = fragmentVisitor.getContinuationToken(); Assert.assertTrue(continuationToken.isPresent()); Assert.assertEquals(continuationTokens.get(segmentCount), continuationToken.get()); } segmentCount++; } } } }

前述範例顯示下列編碼模式:

  • 建立 FragmentMetadataVisitor 剖析資料,以及建立 StreamingMkvReader 提供資料。

  • 對於串流之中的各個 MkvElement,請測試其中繼資料是否為 SIMPLEBLOCK 類型。

  • 如果是,請由 MkvElement 擷取 MkvDataElement

  • MkvDataElement 擷取 Frame (媒體資料)。

  • FragmentMetadataVisitor 擷取 MkvTrackMetadata 用於 Frame

  • FrameMkvTrackMetadata 物件擷取和驗證下列資料:

    • 軌道編號。

    • 影格像素高度。

    • 影格像素寬度。

    • 用於編碼影格轉碼器的轉碼器 ID。

    • 此影格依序抵達。確認前一個影格的音軌編號 (如果存在) 小於目前影格的音軌編號。

如欲在專案使用 FragmentMetadataVisitor,請將 MkvElement 物件以其 accept 方式傳送至訪客:

mkvElement.get().accept(fragmentVisitor);

OutputSegmentMerger

此類別將串流之中不同軌道的中繼資料合併,成為單一區段的串流。

下列程式碼範例 (來自 FragmentMetadataVisitorTest 檔案) 顯示如何使用 OutputSegmentMerger,由名為 inputBytes 的位元組陣列合併軌道中繼資料:

FragmentMetadataVisitor fragmentVisitor = FragmentMetadataVisitor.create(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); OutputSegmentMerger outputSegmentMerger = OutputSegmentMerger.createDefault(outputStream); CompositeMkvElementVisitor compositeVisitor = new TestCompositeVisitor(fragmentVisitor, outputSegmentMerger); final InputStream in = TestResourceUtil.getTestInputStream("output_get_media.mkv"); StreamingMkvReader mkvStreamReader = StreamingMkvReader.createDefault(new InputStreamParserByteSource(in)); while (mkvStreamReader.mightHaveNext()) { Optional<MkvElement> mkvElement = mkvStreamReader.nextIfAvailable(); if (mkvElement.isPresent()) { mkvElement.get().accept(compositeVisitor); if (MkvTypeInfos.SIMPLEBLOCK.equals(mkvElement.get().getElementMetaData().getTypeInfo())) { MkvDataElement dataElement = (MkvDataElement) mkvElement.get(); Frame frame = ((MkvValue<Frame>) dataElement.getValueCopy()).getVal(); Assert.assertTrue(frame.getFrameData().limit() > 0); MkvTrackMetadata trackMetadata = fragmentVisitor.getMkvTrackMetadata(frame.getTrackNumber()); assertTrackAndFragmentInfo(fragmentVisitor, frame, trackMetadata); } }

前述範例顯示下列編碼模式:

  • 建立 FragmentMetadataVisitor 由串流擷取中繼資料。

  • 建立輸出串流以接收合併的中繼資料。

  • 建立 OutputSegmentMerger,在 ByteArrayOutputStream 之中傳送。

  • 建立包含兩個訪客的 CompositeMkvElementVisitor

  • 建立 InputStream 指向指定檔案。

  • 將輸入資料的各個元素合併為輸出串流。

KinesisVideoExample

這是示範如何使用 Kinesis 視訊串流剖析器程式庫的範例應用程式。

此類別執行下列操作:

  • 建立 Kinesis 影片串流。如果特定名稱串流已經存在,串流將遭到刪除並重新建立。

  • 呼叫PutMedia將視訊片段串流至 Kinesis 視訊串流。

  • 呼叫GetMedia將視訊片段串流出 Kinesis 視訊串流。

  • 使用 StreamingMkvReader 剖析在串流傳回的片段,並使用 FragmentMetadataVisitor 記錄片段。

刪除及重新建立串流

下列程式碼範例 (從StreamOps.java檔案) 刪除指定的 Kinesis 視訊串流:

//Delete the stream amazonKinesisVideo.deleteStream(new DeleteStreamRequest().withStreamARN(streamInfo.get().getStreamARN()));

下列程式碼範例 (從StreamOps.java檔案) 建立具有指定名稱的 Kinesis 視訊串流:

amazonKinesisVideo.createStream(new CreateStreamRequest().withStreamName(streamName) .withDataRetentionInHours(DATA_RETENTION_IN_HOURS) .withMediaType("video/h264"));

呼叫 PutMedia

下列程式碼範例 (來自PutMediaWorker.java檔案) PutMedia會呼叫串流:

putMedia.putMedia(new PutMediaRequest().withStreamName(streamName) .withFragmentTimecodeType(FragmentTimecodeType.RELATIVE) .withProducerStartTimestamp(new Date()) .withPayload(inputStream), new PutMediaAckResponseHandler() { ... });

呼叫 GetMedia

下列程式碼範例 (來自GetMediaWorker.java檔案) GetMedia會呼叫串流:

GetMediaResult result = videoMedia.getMedia(new GetMediaRequest().withStreamName(streamName).withStartSelector(startSelector));

解析結 GetMedia 果

本節說明如何使用 StreamingMkvReaderFragmentMetadataVisitorCompositeMkvElementVisitor,以剖析、儲存至檔案,以及記錄 GetMedia 傳回的資料。

讀取 GetMedia 與的輸出 StreamingMkvReader

下面的代碼示例(從GetMediaWorker.java文件)創建一個StreamingMkvReader並使用它來解析GetMedia操作的結果:

StreamingMkvReader mkvStreamReader = StreamingMkvReader.createDefault(new InputStreamParserByteSource(result.getPayload())); log.info("StreamingMkvReader created for stream {} ", streamName); try { mkvStreamReader.apply(this.elementVisitor); } catch (MkvElementVisitException e) { log.error("Exception while accepting visitor {}", e); }

在前述程式碼範例中,StreamingMkvReaderGetMedia 結果的承載擷取 MKVElement 物件。在下節之中,元素將傳送至 FragmentMetadataVisitor

使用檢索片段 FragmentMetadataVisitor

下列程式碼範例 (來自 KinesisVideoExample.javaStreamingMkvReader.java 檔案) 建立 FragmentMetadataVisitorMkvElement 物件 (由 StreamingMkvReader 重複執行) 將以 accept 方法傳送至訪客。

來自 KinesisVideoExample.java

FragmentMetadataVisitor fragmentMetadataVisitor = FragmentMetadataVisitor.create();

來自 StreamingMkvReader.java

if (mkvElementOptional.isPresent()) { //Apply the MkvElement to the visitor mkvElementOptional.get().accept(elementVisitor); }

記錄元素並將其寫入檔案

下列程式碼範例 (來自 KinesisVideoExample.java 檔案) 建立下列物件,並將其以 GetMediaProcessingArguments 函數傳回值的一部分傳回:

  • LogVisitor (MkvElementVisitor 延伸) 寫入系統記錄。

  • OutputStream 將傳入資料寫入 MKV 檔案。

  • BufferedOutputStream 針對 OutputStream 緩衝資料邊界。

  • OutputSegmentMerger 以相同軌道及 EBML 資料合併 GetMedia 結果之中的連續元素。

  • CompositeMkvElementVisitorFragmentMetadataVisitorOutputSegmentMergerLogVisitor到單一元素訪客的 A。

//A visitor used to log as the GetMedia stream is processed. LogVisitor logVisitor = new LogVisitor(fragmentMetadataVisitor); //An OutputSegmentMerger to combine multiple segments that share track and ebml metadata into one //mkv segment. OutputStream fileOutputStream = Files.newOutputStream(Paths.get("kinesis_video_example_merged_output2.mkv"), StandardOpenOption.WRITE, StandardOpenOption.CREATE); BufferedOutputStream outputStream = new BufferedOutputStream(fileOutputStream); OutputSegmentMerger outputSegmentMerger = OutputSegmentMerger.createDefault(outputStream); //A composite visitor to encapsulate the three visitors. CompositeMkvElementVisitor mkvElementVisitor = new CompositeMkvElementVisitor(fragmentMetadataVisitor, outputSegmentMerger, logVisitor); return new GetMediaProcessingArguments(outputStream, logVisitor, mkvElementVisitor);

然後將媒體處理引數傳遞到GetMediaWorker,該引數又傳遞給ExecutorService,該引數會在單獨的執行緒上執行 Worker:

GetMediaWorker getMediaWorker = GetMediaWorker.create(getRegion(), getCredentialsProvider(), getStreamName(), new StartSelector().withStartSelectorType(StartSelectorType.EARLIEST), amazonKinesisVideo, getMediaProcessingArgumentsLocal.getMkvElementVisitor()); executorService.submit(getMediaWorker);

下一步驟

步驟 3:運行並驗證代碼