本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。
本節您將檢驗 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 影片串流。
-
從 Kinesis GetMedia 影片串流中串流影片片段的呼叫。
-
使用 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
串流GetMedia上的下列程式碼範例 (來自 GetMediaWorker.java
檔案) 呼叫:
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
緩衝資料邊界。 -
OutputSegmentMerger 將
GetMedia
結果中的連續元素與相同的軌跡和EBML資料合併。 -
將 FragmentMetadataVisitor、 OutputSegmentMerger和 組成
LogVisitor
單一元素訪客CompositeMkvElementVisitor
的 。
//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
,而 會在不同的執行緒上執行工作者:
GetMediaWorker getMediaWorker = GetMediaWorker.create(getRegion(),
getCredentialsProvider(),
getStreamName(),
new StartSelector().withStartSelectorType(StartSelectorType.EARLIEST),
amazonKinesisVideo,
getMediaProcessingArgumentsLocal.getMkvElementVisitor());
executorService.submit(getMediaWorker);