Loading media/libmediatranscoding/build_and_run_all_unit_tests.sh 0 → 100755 +31 −0 Original line number Diff line number Diff line #!/bin/bash # # Script to run all transcoding related tests from subfolders. # Run script from this folder. # if [ -z "$ANDROID_BUILD_TOP" ]; then echo "Android build environment not set" exit -1 fi # ensure we have mm . $ANDROID_BUILD_TOP/build/envsetup.sh mm echo "waiting for device" adb root && adb wait-for-device remount && adb sync SYNC_FINISHED=true # Run the transcoding service tests. pushd tests . build_and_run_all_unit_tests.sh popd # Run the transcoder tests. pushd transcoder/tests/ . build_and_run_all_unit_tests.sh popd media/libmediatranscoding/tests/build_and_run_all_unit_tests.sh +11 −10 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ # Run tests in this directory. # if [ "$SYNC_FINISHED" != true ]; then if [ -z "$ANDROID_BUILD_TOP" ]; then echo "Android build environment not set" exit -1 Loading @@ -16,6 +17,7 @@ mm echo "waiting for device" adb root && adb wait-for-device remount && adb sync fi echo "========================================" Loading @@ -30,4 +32,3 @@ adb shell /data/nativetest/AdjustableMaxPriorityQueue_tests/AdjustableMaxPriorit echo "testing TranscodingJobScheduler" #adb shell /data/nativetest64/TranscodingJobScheduler_tests/TranscodingJobScheduler_tests adb shell /data/nativetest/TranscodingJobScheduler_tests/TranscodingJobScheduler_tests media/libmediatranscoding/transcoder/Android.bp 0 → 100644 +48 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ cc_library_shared { name: "libmediatranscoder", srcs: [ "MediaSampleReaderNDK.cpp", ], shared_libs: [ "libbase", "libcutils", "libmediandk", "libutils", ], export_include_dirs: [ "include", ], cflags: [ "-Werror", "-Wno-error=deprecated-declarations", "-Wall", ], sanitize: { misc_undefined: [ "unsigned-integer-overflow", "signed-integer-overflow", ], cfi: true, }, } media/libmediatranscoding/transcoder/MediaSampleReaderNDK.cpp 0 → 100644 +260 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // #define LOG_NDEBUG 0 #define LOG_TAG "MediaSampleReader" #include <android-base/logging.h> #include <media/MediaSampleReaderNDK.h> #include <algorithm> #include <vector> namespace android { // static std::shared_ptr<MediaSampleReader> MediaSampleReaderNDK::createFromFd(int fd, size_t offset, size_t size) { AMediaExtractor* extractor = AMediaExtractor_new(); if (extractor == nullptr) { LOG(ERROR) << "Unable to allocate AMediaExtractor"; return nullptr; } media_status_t status = AMediaExtractor_setDataSourceFd(extractor, fd, offset, size); if (status != AMEDIA_OK) { LOG(ERROR) << "AMediaExtractor_setDataSourceFd returned error: " << status; AMediaExtractor_delete(extractor); return nullptr; } auto sampleReader = std::shared_ptr<MediaSampleReaderNDK>(new MediaSampleReaderNDK(extractor)); status = sampleReader->init(); if (status != AMEDIA_OK) { LOG(ERROR) << "MediaSampleReaderNDK::init returned error: " << status; return nullptr; } return sampleReader; } MediaSampleReaderNDK::MediaSampleReaderNDK(AMediaExtractor* extractor) : mExtractor(extractor), mTrackCount(AMediaExtractor_getTrackCount(mExtractor)) { if (mTrackCount > 0) { mTrackCursors.resize(mTrackCount); mTrackCursors.resize(mTrackCount); } } media_status_t MediaSampleReaderNDK::init() { for (size_t trackIndex = 0; trackIndex < mTrackCount; trackIndex++) { media_status_t status = AMediaExtractor_selectTrack(mExtractor, trackIndex); if (status != AMEDIA_OK) { LOG(ERROR) << "AMediaExtractor_selectTrack returned error: " << status; return status; } } mExtractorTrackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor); if (mExtractorTrackIndex >= 0) { mTrackCursors[mExtractorTrackIndex].current.set(mExtractorSampleIndex, AMediaExtractor_getSampleTime(mExtractor)); } else if (mTrackCount > 0) { // The extractor track index is only allowed to be invalid if there are no tracks. LOG(ERROR) << "Track index " << mExtractorTrackIndex << " is invalid for track count " << mTrackCount; return AMEDIA_ERROR_MALFORMED; } return AMEDIA_OK; } MediaSampleReaderNDK::~MediaSampleReaderNDK() { if (mExtractor != nullptr) { AMediaExtractor_delete(mExtractor); } } bool MediaSampleReaderNDK::advanceExtractor_l() { // Reset the "next" sample time whenever the extractor advances past a sample that is current, // to ensure that "next" is appropriately updated when the extractor advances over the next // sample of that track. if (mTrackCursors[mExtractorTrackIndex].current.isSet && mTrackCursors[mExtractorTrackIndex].current.index == mExtractorSampleIndex) { mTrackCursors[mExtractorTrackIndex].next.reset(); } if (!AMediaExtractor_advance(mExtractor)) { return false; } mExtractorTrackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor); mExtractorSampleIndex++; SampleCursor& cursor = mTrackCursors[mExtractorTrackIndex]; if (mExtractorSampleIndex > cursor.previous.index) { if (!cursor.current.isSet) { cursor.current.set(mExtractorSampleIndex, AMediaExtractor_getSampleTime(mExtractor)); } else if (!cursor.next.isSet && mExtractorSampleIndex > cursor.current.index) { cursor.next.set(mExtractorSampleIndex, AMediaExtractor_getSampleTime(mExtractor)); } } return true; } media_status_t MediaSampleReaderNDK::seekExtractorBackwards_l(int64_t targetTimeUs, int targetTrackIndex, uint64_t targetSampleIndex) { if (targetSampleIndex > mExtractorSampleIndex) { LOG(ERROR) << "Error: Forward seek is not supported"; return AMEDIA_ERROR_UNSUPPORTED; } // AMediaExtractor supports reading negative timestamps but does not support seeking to them. const int64_t seekToTimeUs = std::max(targetTimeUs, (int64_t)0); media_status_t status = AMediaExtractor_seekTo(mExtractor, seekToTimeUs, AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC); if (status != AMEDIA_OK) { LOG(ERROR) << "Unable to seek to " << seekToTimeUs << ", target " << targetTimeUs; return status; } mExtractorTrackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor); int64_t sampleTimeUs = AMediaExtractor_getSampleTime(mExtractor); while (sampleTimeUs != targetTimeUs || mExtractorTrackIndex != targetTrackIndex) { if (!AMediaExtractor_advance(mExtractor)) { return AMEDIA_ERROR_END_OF_STREAM; } mExtractorTrackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor); sampleTimeUs = AMediaExtractor_getSampleTime(mExtractor); } mExtractorSampleIndex = targetSampleIndex; return AMEDIA_OK; } void MediaSampleReaderNDK::advanceTrack(int trackIndex) { std::scoped_lock lock(mExtractorMutex); if (trackIndex < 0 || trackIndex >= mTrackCount) { LOG(ERROR) << "Invalid trackIndex " << trackIndex << " for trackCount " << mTrackCount; return; } // Note: Positioning the extractor before advancing the track is needed for two reasons: // 1. To enable multiple advances without explicitly letting the extractor catch up. // 2. To prevent the extractor from being farther than "next". (void)positionExtractorForTrack_l(trackIndex); SampleCursor& cursor = mTrackCursors[trackIndex]; cursor.previous = cursor.current; cursor.current = cursor.next; cursor.next.reset(); } media_status_t MediaSampleReaderNDK::positionExtractorForTrack_l(int trackIndex) { media_status_t status = AMEDIA_OK; const SampleCursor& cursor = mTrackCursors[trackIndex]; // Seek backwards if the extractor is ahead of the current time. if (cursor.current.isSet && mExtractorSampleIndex > cursor.current.index) { status = seekExtractorBackwards_l(cursor.current.timeStampUs, trackIndex, cursor.current.index); if (status != AMEDIA_OK) return status; } // Advance until extractor points to the current sample. while (!(cursor.current.isSet && cursor.current.index == mExtractorSampleIndex)) { if (!advanceExtractor_l()) { return AMEDIA_ERROR_END_OF_STREAM; } } return AMEDIA_OK; } media_status_t MediaSampleReaderNDK::getSampleInfoForTrack(int trackIndex, MediaSampleInfo* info) { std::scoped_lock lock(mExtractorMutex); if (trackIndex < 0 || trackIndex >= mTrackCount) { LOG(ERROR) << "Invalid trackIndex " << trackIndex << " for trackCount " << mTrackCount; return AMEDIA_ERROR_INVALID_PARAMETER; } else if (info == nullptr) { LOG(ERROR) << "MediaSampleInfo pointer is NULL."; return AMEDIA_ERROR_INVALID_PARAMETER; } media_status_t status = positionExtractorForTrack_l(trackIndex); if (status == AMEDIA_OK) { info->presentationTimeUs = AMediaExtractor_getSampleTime(mExtractor); info->flags = AMediaExtractor_getSampleFlags(mExtractor); info->size = AMediaExtractor_getSampleSize(mExtractor); } else if (status == AMEDIA_ERROR_END_OF_STREAM) { info->presentationTimeUs = 0; info->flags = SAMPLE_FLAG_END_OF_STREAM; info->size = 0; } return status; } media_status_t MediaSampleReaderNDK::readSampleDataForTrack(int trackIndex, uint8_t* buffer, size_t bufferSize) { std::scoped_lock lock(mExtractorMutex); if (trackIndex < 0 || trackIndex >= mTrackCount) { LOG(ERROR) << "Invalid trackIndex " << trackIndex << " for trackCount " << mTrackCount; return AMEDIA_ERROR_INVALID_PARAMETER; } else if (buffer == nullptr) { LOG(ERROR) << "buffer pointer is NULL"; return AMEDIA_ERROR_INVALID_PARAMETER; } media_status_t status = positionExtractorForTrack_l(trackIndex); if (status != AMEDIA_OK) return status; ssize_t sampleSize = AMediaExtractor_getSampleSize(mExtractor); if (bufferSize < sampleSize) { LOG(ERROR) << "Buffer is too small for sample, " << bufferSize << " vs " << sampleSize; return AMEDIA_ERROR_INVALID_PARAMETER; } ssize_t bytesRead = AMediaExtractor_readSampleData(mExtractor, buffer, bufferSize); if (bytesRead < sampleSize) { LOG(ERROR) << "Unable to read full sample, " << bytesRead << " vs " << sampleSize; return AMEDIA_ERROR_INVALID_PARAMETER; } return AMEDIA_OK; } AMediaFormat* MediaSampleReaderNDK::getFileFormat() { return AMediaExtractor_getFileFormat(mExtractor); } size_t MediaSampleReaderNDK::getTrackCount() const { return mTrackCount; } AMediaFormat* MediaSampleReaderNDK::getTrackFormat(int trackIndex) { if (trackIndex < 0 || trackIndex >= mTrackCount) { LOG(ERROR) << "Invalid trackIndex " << trackIndex << " for trackCount " << mTrackCount; return AMediaFormat_new(); } return AMediaExtractor_getTrackFormat(mExtractor, trackIndex); } } // namespace android media/libmediatranscoding/transcoder/include/media/MediaSample.h 0 → 100644 +94 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_MEDIA_SAMPLE_H #define ANDROID_MEDIA_SAMPLE_H #include <cstdint> namespace android { /** * Media sample flags. * These flags purposely match the media NDK's buffer and extractor flags with one exception. The * NDK extractor's flag for encrypted samples (AMEDIAEXTRACTOR_SAMPLE_FLAG_ENCRYPTED) is equal to 2, * i.e. the same as SAMPLE_FLAG_CODEC_CONFIG below and NDK's AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG. * Sample producers based on the NDK's extractor is responsible for catching those values. * Note that currently the media transcoder does not support encrypted samples. */ enum : uint32_t { SAMPLE_FLAG_SYNC_SAMPLE = 1, SAMPLE_FLAG_CODEC_CONFIG = 2, SAMPLE_FLAG_END_OF_STREAM = 4, SAMPLE_FLAG_PARTIAL_FRAME = 8, }; // Check that the sample flags have the expected NDK meaning. namespace { #include <media/NdkMediaCodec.h> #include <media/NdkMediaExtractor.h> static_assert(SAMPLE_FLAG_SYNC_SAMPLE == AMEDIAEXTRACTOR_SAMPLE_FLAG_SYNC, "Sample flag mismatch: SYNC_SAMPLE"); static_assert(SAMPLE_FLAG_CODEC_CONFIG == AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG, "Sample flag mismatch: CODEC_CONFIG"); static_assert(SAMPLE_FLAG_END_OF_STREAM == AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM, "Sample flag mismatch: END_OF_STREAM"); static_assert(SAMPLE_FLAG_PARTIAL_FRAME == AMEDIACODEC_BUFFER_FLAG_PARTIAL_FRAME, "Sample flag mismatch: PARTIAL_FRAME"); } // anonymous namespace /** * MediaSampleInfo is an object that carries information about a compressed media sample without * holding any sample data. */ struct MediaSampleInfo { /** The sample's presentation timestamp in microseconds. */ int64_t presentationTimeUs = 0; /** The size of the compressed sample data in bytes. */ size_t size = 0; /** Sample flags. */ uint32_t flags = 0; }; /** * MediaSample holds a compressed media sample in memory. */ struct MediaSample { /** * Byte buffer containing the sample's compressed data. * The memory backing this buffer is not managed by the MediaSample object so a separate * mechanism to release a buffer is needed between a producer and a consumer. */ const uint8_t* buffer = nullptr; /** Offset, in bytes, to the sample's compressed data inside the buffer. */ size_t dataOffset = 0; /** * Buffer identifier. This identifier is likely only meaningful to the sample data producer and * can be used for reclaiming the buffer once a consumer is done processing it. */ uint32_t bufferId = 0xBAADF00D; /** Media sample information. */ MediaSampleInfo info; }; } // namespace android #endif // ANDROID_MEDIA_SAMPLE_H Loading
media/libmediatranscoding/build_and_run_all_unit_tests.sh 0 → 100755 +31 −0 Original line number Diff line number Diff line #!/bin/bash # # Script to run all transcoding related tests from subfolders. # Run script from this folder. # if [ -z "$ANDROID_BUILD_TOP" ]; then echo "Android build environment not set" exit -1 fi # ensure we have mm . $ANDROID_BUILD_TOP/build/envsetup.sh mm echo "waiting for device" adb root && adb wait-for-device remount && adb sync SYNC_FINISHED=true # Run the transcoding service tests. pushd tests . build_and_run_all_unit_tests.sh popd # Run the transcoder tests. pushd transcoder/tests/ . build_and_run_all_unit_tests.sh popd
media/libmediatranscoding/tests/build_and_run_all_unit_tests.sh +11 −10 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ # Run tests in this directory. # if [ "$SYNC_FINISHED" != true ]; then if [ -z "$ANDROID_BUILD_TOP" ]; then echo "Android build environment not set" exit -1 Loading @@ -16,6 +17,7 @@ mm echo "waiting for device" adb root && adb wait-for-device remount && adb sync fi echo "========================================" Loading @@ -30,4 +32,3 @@ adb shell /data/nativetest/AdjustableMaxPriorityQueue_tests/AdjustableMaxPriorit echo "testing TranscodingJobScheduler" #adb shell /data/nativetest64/TranscodingJobScheduler_tests/TranscodingJobScheduler_tests adb shell /data/nativetest/TranscodingJobScheduler_tests/TranscodingJobScheduler_tests
media/libmediatranscoding/transcoder/Android.bp 0 → 100644 +48 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ cc_library_shared { name: "libmediatranscoder", srcs: [ "MediaSampleReaderNDK.cpp", ], shared_libs: [ "libbase", "libcutils", "libmediandk", "libutils", ], export_include_dirs: [ "include", ], cflags: [ "-Werror", "-Wno-error=deprecated-declarations", "-Wall", ], sanitize: { misc_undefined: [ "unsigned-integer-overflow", "signed-integer-overflow", ], cfi: true, }, }
media/libmediatranscoding/transcoder/MediaSampleReaderNDK.cpp 0 → 100644 +260 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // #define LOG_NDEBUG 0 #define LOG_TAG "MediaSampleReader" #include <android-base/logging.h> #include <media/MediaSampleReaderNDK.h> #include <algorithm> #include <vector> namespace android { // static std::shared_ptr<MediaSampleReader> MediaSampleReaderNDK::createFromFd(int fd, size_t offset, size_t size) { AMediaExtractor* extractor = AMediaExtractor_new(); if (extractor == nullptr) { LOG(ERROR) << "Unable to allocate AMediaExtractor"; return nullptr; } media_status_t status = AMediaExtractor_setDataSourceFd(extractor, fd, offset, size); if (status != AMEDIA_OK) { LOG(ERROR) << "AMediaExtractor_setDataSourceFd returned error: " << status; AMediaExtractor_delete(extractor); return nullptr; } auto sampleReader = std::shared_ptr<MediaSampleReaderNDK>(new MediaSampleReaderNDK(extractor)); status = sampleReader->init(); if (status != AMEDIA_OK) { LOG(ERROR) << "MediaSampleReaderNDK::init returned error: " << status; return nullptr; } return sampleReader; } MediaSampleReaderNDK::MediaSampleReaderNDK(AMediaExtractor* extractor) : mExtractor(extractor), mTrackCount(AMediaExtractor_getTrackCount(mExtractor)) { if (mTrackCount > 0) { mTrackCursors.resize(mTrackCount); mTrackCursors.resize(mTrackCount); } } media_status_t MediaSampleReaderNDK::init() { for (size_t trackIndex = 0; trackIndex < mTrackCount; trackIndex++) { media_status_t status = AMediaExtractor_selectTrack(mExtractor, trackIndex); if (status != AMEDIA_OK) { LOG(ERROR) << "AMediaExtractor_selectTrack returned error: " << status; return status; } } mExtractorTrackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor); if (mExtractorTrackIndex >= 0) { mTrackCursors[mExtractorTrackIndex].current.set(mExtractorSampleIndex, AMediaExtractor_getSampleTime(mExtractor)); } else if (mTrackCount > 0) { // The extractor track index is only allowed to be invalid if there are no tracks. LOG(ERROR) << "Track index " << mExtractorTrackIndex << " is invalid for track count " << mTrackCount; return AMEDIA_ERROR_MALFORMED; } return AMEDIA_OK; } MediaSampleReaderNDK::~MediaSampleReaderNDK() { if (mExtractor != nullptr) { AMediaExtractor_delete(mExtractor); } } bool MediaSampleReaderNDK::advanceExtractor_l() { // Reset the "next" sample time whenever the extractor advances past a sample that is current, // to ensure that "next" is appropriately updated when the extractor advances over the next // sample of that track. if (mTrackCursors[mExtractorTrackIndex].current.isSet && mTrackCursors[mExtractorTrackIndex].current.index == mExtractorSampleIndex) { mTrackCursors[mExtractorTrackIndex].next.reset(); } if (!AMediaExtractor_advance(mExtractor)) { return false; } mExtractorTrackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor); mExtractorSampleIndex++; SampleCursor& cursor = mTrackCursors[mExtractorTrackIndex]; if (mExtractorSampleIndex > cursor.previous.index) { if (!cursor.current.isSet) { cursor.current.set(mExtractorSampleIndex, AMediaExtractor_getSampleTime(mExtractor)); } else if (!cursor.next.isSet && mExtractorSampleIndex > cursor.current.index) { cursor.next.set(mExtractorSampleIndex, AMediaExtractor_getSampleTime(mExtractor)); } } return true; } media_status_t MediaSampleReaderNDK::seekExtractorBackwards_l(int64_t targetTimeUs, int targetTrackIndex, uint64_t targetSampleIndex) { if (targetSampleIndex > mExtractorSampleIndex) { LOG(ERROR) << "Error: Forward seek is not supported"; return AMEDIA_ERROR_UNSUPPORTED; } // AMediaExtractor supports reading negative timestamps but does not support seeking to them. const int64_t seekToTimeUs = std::max(targetTimeUs, (int64_t)0); media_status_t status = AMediaExtractor_seekTo(mExtractor, seekToTimeUs, AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC); if (status != AMEDIA_OK) { LOG(ERROR) << "Unable to seek to " << seekToTimeUs << ", target " << targetTimeUs; return status; } mExtractorTrackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor); int64_t sampleTimeUs = AMediaExtractor_getSampleTime(mExtractor); while (sampleTimeUs != targetTimeUs || mExtractorTrackIndex != targetTrackIndex) { if (!AMediaExtractor_advance(mExtractor)) { return AMEDIA_ERROR_END_OF_STREAM; } mExtractorTrackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor); sampleTimeUs = AMediaExtractor_getSampleTime(mExtractor); } mExtractorSampleIndex = targetSampleIndex; return AMEDIA_OK; } void MediaSampleReaderNDK::advanceTrack(int trackIndex) { std::scoped_lock lock(mExtractorMutex); if (trackIndex < 0 || trackIndex >= mTrackCount) { LOG(ERROR) << "Invalid trackIndex " << trackIndex << " for trackCount " << mTrackCount; return; } // Note: Positioning the extractor before advancing the track is needed for two reasons: // 1. To enable multiple advances without explicitly letting the extractor catch up. // 2. To prevent the extractor from being farther than "next". (void)positionExtractorForTrack_l(trackIndex); SampleCursor& cursor = mTrackCursors[trackIndex]; cursor.previous = cursor.current; cursor.current = cursor.next; cursor.next.reset(); } media_status_t MediaSampleReaderNDK::positionExtractorForTrack_l(int trackIndex) { media_status_t status = AMEDIA_OK; const SampleCursor& cursor = mTrackCursors[trackIndex]; // Seek backwards if the extractor is ahead of the current time. if (cursor.current.isSet && mExtractorSampleIndex > cursor.current.index) { status = seekExtractorBackwards_l(cursor.current.timeStampUs, trackIndex, cursor.current.index); if (status != AMEDIA_OK) return status; } // Advance until extractor points to the current sample. while (!(cursor.current.isSet && cursor.current.index == mExtractorSampleIndex)) { if (!advanceExtractor_l()) { return AMEDIA_ERROR_END_OF_STREAM; } } return AMEDIA_OK; } media_status_t MediaSampleReaderNDK::getSampleInfoForTrack(int trackIndex, MediaSampleInfo* info) { std::scoped_lock lock(mExtractorMutex); if (trackIndex < 0 || trackIndex >= mTrackCount) { LOG(ERROR) << "Invalid trackIndex " << trackIndex << " for trackCount " << mTrackCount; return AMEDIA_ERROR_INVALID_PARAMETER; } else if (info == nullptr) { LOG(ERROR) << "MediaSampleInfo pointer is NULL."; return AMEDIA_ERROR_INVALID_PARAMETER; } media_status_t status = positionExtractorForTrack_l(trackIndex); if (status == AMEDIA_OK) { info->presentationTimeUs = AMediaExtractor_getSampleTime(mExtractor); info->flags = AMediaExtractor_getSampleFlags(mExtractor); info->size = AMediaExtractor_getSampleSize(mExtractor); } else if (status == AMEDIA_ERROR_END_OF_STREAM) { info->presentationTimeUs = 0; info->flags = SAMPLE_FLAG_END_OF_STREAM; info->size = 0; } return status; } media_status_t MediaSampleReaderNDK::readSampleDataForTrack(int trackIndex, uint8_t* buffer, size_t bufferSize) { std::scoped_lock lock(mExtractorMutex); if (trackIndex < 0 || trackIndex >= mTrackCount) { LOG(ERROR) << "Invalid trackIndex " << trackIndex << " for trackCount " << mTrackCount; return AMEDIA_ERROR_INVALID_PARAMETER; } else if (buffer == nullptr) { LOG(ERROR) << "buffer pointer is NULL"; return AMEDIA_ERROR_INVALID_PARAMETER; } media_status_t status = positionExtractorForTrack_l(trackIndex); if (status != AMEDIA_OK) return status; ssize_t sampleSize = AMediaExtractor_getSampleSize(mExtractor); if (bufferSize < sampleSize) { LOG(ERROR) << "Buffer is too small for sample, " << bufferSize << " vs " << sampleSize; return AMEDIA_ERROR_INVALID_PARAMETER; } ssize_t bytesRead = AMediaExtractor_readSampleData(mExtractor, buffer, bufferSize); if (bytesRead < sampleSize) { LOG(ERROR) << "Unable to read full sample, " << bytesRead << " vs " << sampleSize; return AMEDIA_ERROR_INVALID_PARAMETER; } return AMEDIA_OK; } AMediaFormat* MediaSampleReaderNDK::getFileFormat() { return AMediaExtractor_getFileFormat(mExtractor); } size_t MediaSampleReaderNDK::getTrackCount() const { return mTrackCount; } AMediaFormat* MediaSampleReaderNDK::getTrackFormat(int trackIndex) { if (trackIndex < 0 || trackIndex >= mTrackCount) { LOG(ERROR) << "Invalid trackIndex " << trackIndex << " for trackCount " << mTrackCount; return AMediaFormat_new(); } return AMediaExtractor_getTrackFormat(mExtractor, trackIndex); } } // namespace android
media/libmediatranscoding/transcoder/include/media/MediaSample.h 0 → 100644 +94 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_MEDIA_SAMPLE_H #define ANDROID_MEDIA_SAMPLE_H #include <cstdint> namespace android { /** * Media sample flags. * These flags purposely match the media NDK's buffer and extractor flags with one exception. The * NDK extractor's flag for encrypted samples (AMEDIAEXTRACTOR_SAMPLE_FLAG_ENCRYPTED) is equal to 2, * i.e. the same as SAMPLE_FLAG_CODEC_CONFIG below and NDK's AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG. * Sample producers based on the NDK's extractor is responsible for catching those values. * Note that currently the media transcoder does not support encrypted samples. */ enum : uint32_t { SAMPLE_FLAG_SYNC_SAMPLE = 1, SAMPLE_FLAG_CODEC_CONFIG = 2, SAMPLE_FLAG_END_OF_STREAM = 4, SAMPLE_FLAG_PARTIAL_FRAME = 8, }; // Check that the sample flags have the expected NDK meaning. namespace { #include <media/NdkMediaCodec.h> #include <media/NdkMediaExtractor.h> static_assert(SAMPLE_FLAG_SYNC_SAMPLE == AMEDIAEXTRACTOR_SAMPLE_FLAG_SYNC, "Sample flag mismatch: SYNC_SAMPLE"); static_assert(SAMPLE_FLAG_CODEC_CONFIG == AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG, "Sample flag mismatch: CODEC_CONFIG"); static_assert(SAMPLE_FLAG_END_OF_STREAM == AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM, "Sample flag mismatch: END_OF_STREAM"); static_assert(SAMPLE_FLAG_PARTIAL_FRAME == AMEDIACODEC_BUFFER_FLAG_PARTIAL_FRAME, "Sample flag mismatch: PARTIAL_FRAME"); } // anonymous namespace /** * MediaSampleInfo is an object that carries information about a compressed media sample without * holding any sample data. */ struct MediaSampleInfo { /** The sample's presentation timestamp in microseconds. */ int64_t presentationTimeUs = 0; /** The size of the compressed sample data in bytes. */ size_t size = 0; /** Sample flags. */ uint32_t flags = 0; }; /** * MediaSample holds a compressed media sample in memory. */ struct MediaSample { /** * Byte buffer containing the sample's compressed data. * The memory backing this buffer is not managed by the MediaSample object so a separate * mechanism to release a buffer is needed between a producer and a consumer. */ const uint8_t* buffer = nullptr; /** Offset, in bytes, to the sample's compressed data inside the buffer. */ size_t dataOffset = 0; /** * Buffer identifier. This identifier is likely only meaningful to the sample data producer and * can be used for reclaiming the buffer once a consumer is done processing it. */ uint32_t bufferId = 0xBAADF00D; /** Media sample information. */ MediaSampleInfo info; }; } // namespace android #endif // ANDROID_MEDIA_SAMPLE_H