本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。
撰寫並檢查程式碼
本節您將檢驗 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
檔案) 顯示如何使用 FragmentMetadataVisitor
由 MkvElement
物件擷取資料:
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
。 -
由
Frame
及MkvTrackMetadata
物件擷取和驗證下列資料:-
軌道編號。
-
影格像素高度。
-
影格像素寬度。
-
用於編碼影格轉碼器的轉碼器 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 果
本節說明如何使用 StreamingMkvReader、FragmentMetadataVisitor 及 CompositeMkvElementVisitor
,以剖析、儲存至檔案,以及記錄 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); }
在前述程式碼範例中,StreamingMkvReader 由 GetMedia
結果的承載擷取 MKVElement
物件。在下節之中,元素將傳送至 FragmentMetadataVisitor。
使用檢索片段 FragmentMetadataVisitor
下列程式碼範例 (來自 KinesisVideoExample.java
及 StreamingMkvReader.java
檔案) 建立 FragmentMetadataVisitor。MkvElement
物件 (由 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
緩衝資料邊界。 -
將
GetMedia
結果中OutputSegmentMerger的連續元素與相同的軌跡和EBML資料合併。 -
組
CompositeMkvElementVisitor
成FragmentMetadataVisitor、OutputSegmentMerger和LogVisitor
到單一元素訪客的 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);