| /* |
| * Copyright 2021 The libgav1 Authors |
| * |
| * 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. |
| */ |
| |
| #ifdef __cplusplus |
| #error Do not compile this file with a C++ compiler |
| #endif |
| |
| // clang-format off |
| #include "src/gav1/decoder.h" |
| |
| // Import the test frame #defines. |
| #include "src/decoder_test_data.h" |
| // clang-format on |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #define ASSERT_EQ(a, b) \ |
| do { \ |
| if ((a) != (b)) { \ |
| fprintf(stderr, "Assertion failure: (%s) == (%s), at %s:%d\n", #a, #b, \ |
| __FILE__, __LINE__); \ |
| fprintf(stderr, "C DecoderTest failed\n"); \ |
| exit(1); \ |
| } \ |
| } while (0) |
| |
| #define ASSERT_NE(a, b) \ |
| do { \ |
| if ((a) == (b)) { \ |
| fprintf(stderr, "Assertion failure: (%s) != (%s), at %s:%d\n", #a, #b, \ |
| __FILE__, __LINE__); \ |
| fprintf(stderr, "C DecoderTest failed\n"); \ |
| exit(1); \ |
| } \ |
| } while (0) |
| |
| #define ASSERT_TRUE(a) \ |
| do { \ |
| if (!(a)) { \ |
| fprintf(stderr, "Assertion failure: %s, at %s:%d\n", #a, __FILE__, \ |
| __LINE__); \ |
| fprintf(stderr, "C DecoderTest failed\n"); \ |
| exit(1); \ |
| } \ |
| } while (0) |
| |
| #define ASSERT_FALSE(a) \ |
| do { \ |
| if (a) { \ |
| fprintf(stderr, "Assertion failure: !(%s), at %s:%d\n", #a, __FILE__, \ |
| __LINE__); \ |
| fprintf(stderr, "C DecoderTest failed\n"); \ |
| exit(1); \ |
| } \ |
| } while (0) |
| |
| static const uint8_t kFrame1[] = {OBU_TEMPORAL_DELIMITER, OBU_SEQUENCE_HEADER, |
| OBU_FRAME_1}; |
| |
| static const uint8_t kFrame2[] = {OBU_TEMPORAL_DELIMITER, OBU_FRAME_2}; |
| |
| typedef struct DecoderTest { |
| Libgav1Decoder* decoder; |
| int frames_in_use; |
| void* buffer_private_data; |
| void* released_input_buffer; |
| } DecoderTest; |
| |
| static void DecoderTestInit(DecoderTest* test) { |
| test->decoder = NULL; |
| test->frames_in_use = 0; |
| test->buffer_private_data = NULL; |
| test->released_input_buffer = NULL; |
| } |
| |
| static void DecoderTestIncrementFramesInUse(DecoderTest* test) { |
| ++test->frames_in_use; |
| } |
| |
| static void DecoderTestDecrementFramesInUse(DecoderTest* test) { |
| --test->frames_in_use; |
| } |
| |
| static void DecoderTestSetReleasedInputBuffer(DecoderTest* test, |
| void* released_input_buffer) { |
| test->released_input_buffer = released_input_buffer; |
| } |
| |
| static void DecoderTestSetBufferPrivateData(DecoderTest* test, |
| void* buffer_private_data) { |
| test->buffer_private_data = buffer_private_data; |
| } |
| |
| typedef struct FrameBufferPrivate { |
| uint8_t* data[3]; |
| } FrameBufferPrivate; |
| |
| static Libgav1StatusCode GetFrameBuffer( |
| void* callback_private_data, int bitdepth, Libgav1ImageFormat image_format, |
| int width, int height, int left_border, int right_border, int top_border, |
| int bottom_border, int stride_alignment, Libgav1FrameBuffer* frame_buffer) { |
| Libgav1FrameBufferInfo info; |
| Libgav1StatusCode status = Libgav1ComputeFrameBufferInfo( |
| bitdepth, image_format, width, height, left_border, right_border, |
| top_border, bottom_border, stride_alignment, &info); |
| if (status != kLibgav1StatusOk) return status; |
| |
| FrameBufferPrivate* buffer_private = |
| (FrameBufferPrivate*)malloc(sizeof(FrameBufferPrivate)); |
| if (buffer_private == NULL) return kLibgav1StatusOutOfMemory; |
| |
| for (int i = 0; i < 3; ++i) { |
| const size_t size = (i == 0) ? info.y_buffer_size : info.uv_buffer_size; |
| buffer_private->data[i] = (uint8_t*)malloc(sizeof(uint8_t) * size); |
| if (buffer_private->data[i] == NULL) { |
| for (int j = 0; j < i; j++) { |
| free(buffer_private->data[j]); |
| } |
| free(buffer_private); |
| return kLibgav1StatusOutOfMemory; |
| } |
| } |
| |
| uint8_t* const y_buffer = buffer_private->data[0]; |
| uint8_t* const u_buffer = |
| (info.uv_buffer_size != 0) ? buffer_private->data[1] : NULL; |
| uint8_t* const v_buffer = |
| (info.uv_buffer_size != 0) ? buffer_private->data[2] : NULL; |
| |
| status = Libgav1SetFrameBuffer(&info, y_buffer, u_buffer, v_buffer, |
| buffer_private, frame_buffer); |
| if (status != kLibgav1StatusOk) return status; |
| |
| DecoderTest* const decoder_test = (DecoderTest*)callback_private_data; |
| DecoderTestIncrementFramesInUse(decoder_test); |
| DecoderTestSetBufferPrivateData(decoder_test, frame_buffer->private_data); |
| return kLibgav1StatusOk; |
| } |
| |
| static void ReleaseFrameBuffer(void* callback_private_data, |
| void* buffer_private_data) { |
| FrameBufferPrivate* buffer_private = (FrameBufferPrivate*)buffer_private_data; |
| for (int i = 0; i < 3; ++i) { |
| free(buffer_private->data[i]); |
| } |
| free(buffer_private); |
| DecoderTest* const decoder_test = (DecoderTest*)callback_private_data; |
| DecoderTestDecrementFramesInUse(decoder_test); |
| } |
| |
| static void ReleaseInputBuffer(void* private_data, void* input_buffer) { |
| DecoderTestSetReleasedInputBuffer((DecoderTest*)private_data, input_buffer); |
| } |
| |
| static void DecoderTestSetUp(DecoderTest* test) { |
| Libgav1DecoderSettings settings; |
| Libgav1DecoderSettingsInitDefault(&settings); |
| settings.frame_parallel = 0; // false |
| settings.get_frame_buffer = GetFrameBuffer; |
| settings.release_frame_buffer = ReleaseFrameBuffer; |
| settings.callback_private_data = test; |
| settings.release_input_buffer = ReleaseInputBuffer; |
| ASSERT_EQ(test->decoder, NULL); |
| ASSERT_EQ(Libgav1DecoderCreate(&settings, &test->decoder), kLibgav1StatusOk); |
| ASSERT_NE(test->decoder, NULL); |
| } |
| |
| static void DecoderTestAPIFlowForNonFrameParallelMode(void) { |
| DecoderTest test; |
| DecoderTestInit(&test); |
| DecoderTestSetUp(&test); |
| |
| Libgav1StatusCode status; |
| const Libgav1DecoderBuffer* buffer; |
| |
| // Enqueue frame1 for decoding. |
| status = Libgav1DecoderEnqueueFrame(test.decoder, kFrame1, sizeof(kFrame1), 0, |
| (uint8_t*)&kFrame1); |
| ASSERT_EQ(status, kLibgav1StatusOk); |
| |
| // In non-frame-parallel mode, decoding happens only in the DequeueFrame call. |
| // So there should be no frames in use yet. |
| ASSERT_EQ(test.frames_in_use, 0); |
| |
| // Dequeue the output of frame1. |
| status = Libgav1DecoderDequeueFrame(test.decoder, &buffer); |
| ASSERT_EQ(status, kLibgav1StatusOk); |
| ASSERT_NE(buffer, NULL); |
| ASSERT_EQ(test.released_input_buffer, &kFrame1); |
| |
| // libgav1 has decoded frame1 and is holding a reference to it. |
| ASSERT_EQ(test.frames_in_use, 1); |
| ASSERT_EQ(test.buffer_private_data, buffer->buffer_private_data); |
| |
| // Enqueue frame2 for decoding. |
| status = Libgav1DecoderEnqueueFrame(test.decoder, kFrame2, sizeof(kFrame2), 0, |
| (uint8_t*)&kFrame2); |
| ASSERT_EQ(status, kLibgav1StatusOk); |
| |
| ASSERT_EQ(test.frames_in_use, 1); |
| |
| // Dequeue the output of frame2. |
| status = Libgav1DecoderDequeueFrame(test.decoder, &buffer); |
| ASSERT_EQ(status, kLibgav1StatusOk); |
| ASSERT_NE(buffer, NULL); |
| ASSERT_EQ(test.released_input_buffer, &kFrame2); |
| |
| ASSERT_EQ(test.frames_in_use, 2); |
| ASSERT_EQ(test.buffer_private_data, buffer->buffer_private_data); |
| |
| // Signal end of stream (method 1). This should ensure that all the references |
| // are released. |
| status = Libgav1DecoderSignalEOS(test.decoder); |
| |
| // libgav1 should have released all the reference frames now. |
| ASSERT_EQ(test.frames_in_use, 0); |
| |
| // Now, the decoder is ready to accept a new coded video sequence. |
| |
| // Enqueue frame1 for decoding. |
| status = Libgav1DecoderEnqueueFrame(test.decoder, kFrame1, sizeof(kFrame1), 0, |
| (uint8_t*)&kFrame1); |
| ASSERT_EQ(status, kLibgav1StatusOk); |
| |
| ASSERT_EQ(test.frames_in_use, 0); |
| |
| // Dequeue the output of frame1. |
| status = Libgav1DecoderDequeueFrame(test.decoder, &buffer); |
| ASSERT_EQ(status, kLibgav1StatusOk); |
| ASSERT_NE(buffer, NULL); |
| ASSERT_EQ(test.released_input_buffer, &kFrame1); |
| |
| ASSERT_EQ(test.frames_in_use, 1); |
| ASSERT_EQ(test.buffer_private_data, buffer->buffer_private_data); |
| |
| // Enqueue frame2 for decoding. |
| status = Libgav1DecoderEnqueueFrame(test.decoder, kFrame2, sizeof(kFrame2), 0, |
| (uint8_t*)&kFrame2); |
| ASSERT_EQ(status, kLibgav1StatusOk); |
| |
| ASSERT_EQ(test.frames_in_use, 1); |
| |
| // Dequeue the output of frame2. |
| status = Libgav1DecoderDequeueFrame(test.decoder, &buffer); |
| ASSERT_EQ(status, kLibgav1StatusOk); |
| ASSERT_NE(buffer, NULL); |
| ASSERT_EQ(test.released_input_buffer, &kFrame2); |
| |
| ASSERT_EQ(test.frames_in_use, 2); |
| ASSERT_EQ(test.buffer_private_data, buffer->buffer_private_data); |
| |
| // Signal end of stream (method 2). This should ensure that all the references |
| // are released. |
| Libgav1DecoderDestroy(test.decoder); |
| test.decoder = NULL; |
| |
| // libgav1 should have released all the frames now. |
| ASSERT_EQ(test.frames_in_use, 0); |
| } |
| |
| static void |
| DecoderTestNonFrameParallelModeEnqueueMultipleFramesWithoutDequeuing(void) { |
| DecoderTest test; |
| DecoderTestInit(&test); |
| DecoderTestSetUp(&test); |
| |
| Libgav1StatusCode status; |
| const Libgav1DecoderBuffer* buffer; |
| |
| // Enqueue frame1 for decoding. |
| status = Libgav1DecoderEnqueueFrame(test.decoder, kFrame1, sizeof(kFrame1), 0, |
| (uint8_t*)&kFrame1); |
| ASSERT_EQ(status, kLibgav1StatusOk); |
| |
| // Until the output of frame1 is dequeued, no other frames can be enqueued. |
| status = Libgav1DecoderEnqueueFrame(test.decoder, kFrame2, sizeof(kFrame2), 0, |
| (uint8_t*)&kFrame2); |
| ASSERT_EQ(status, kLibgav1StatusTryAgain); |
| |
| ASSERT_EQ(test.frames_in_use, 0); |
| |
| // Dequeue the output of frame1. |
| status = Libgav1DecoderDequeueFrame(test.decoder, &buffer); |
| ASSERT_EQ(status, kLibgav1StatusOk); |
| ASSERT_NE(buffer, NULL); |
| ASSERT_EQ(test.released_input_buffer, &kFrame1); |
| |
| ASSERT_EQ(test.frames_in_use, 1); |
| |
| // Delete the decoder instance. |
| Libgav1DecoderDestroy(test.decoder); |
| test.decoder = NULL; |
| |
| ASSERT_EQ(test.frames_in_use, 0); |
| } |
| |
| static void DecoderTestNonFrameParallelModeEOSBeforeDequeuingLastFrame(void) { |
| DecoderTest test; |
| DecoderTestInit(&test); |
| DecoderTestSetUp(&test); |
| |
| Libgav1StatusCode status; |
| const Libgav1DecoderBuffer* buffer; |
| |
| // Enqueue frame1 for decoding. |
| status = Libgav1DecoderEnqueueFrame(test.decoder, kFrame1, sizeof(kFrame1), 0, |
| (uint8_t*)&kFrame1); |
| ASSERT_EQ(status, kLibgav1StatusOk); |
| |
| ASSERT_EQ(test.frames_in_use, 0); |
| |
| // Dequeue the output of frame1. |
| status = Libgav1DecoderDequeueFrame(test.decoder, &buffer); |
| ASSERT_EQ(status, kLibgav1StatusOk); |
| ASSERT_NE(buffer, NULL); |
| ASSERT_EQ(test.released_input_buffer, &kFrame1); |
| |
| // Enqueue frame2 for decoding. |
| status = Libgav1DecoderEnqueueFrame(test.decoder, kFrame2, sizeof(kFrame2), 0, |
| (uint8_t*)&kFrame2); |
| ASSERT_EQ(status, kLibgav1StatusOk); |
| |
| ASSERT_EQ(test.frames_in_use, 1); |
| |
| // Signal end of stream before dequeuing the output of frame2. |
| status = Libgav1DecoderSignalEOS(test.decoder); |
| ASSERT_EQ(status, kLibgav1StatusOk); |
| |
| // In this case, the output of the last frame that was enqueued is lost (which |
| // is intentional since end of stream was signaled without dequeueing it). |
| ASSERT_EQ(test.frames_in_use, 0); |
| |
| Libgav1DecoderDestroy(test.decoder); |
| test.decoder = NULL; |
| } |
| |
| static void DecoderTestNonFrameParallelModeInvalidFrameAfterEOS(void) { |
| DecoderTest test; |
| DecoderTestInit(&test); |
| DecoderTestSetUp(&test); |
| |
| Libgav1StatusCode status; |
| const Libgav1DecoderBuffer* buffer = NULL; |
| |
| // Enqueue frame1 for decoding. |
| status = Libgav1DecoderEnqueueFrame(test.decoder, kFrame1, sizeof(kFrame1), 0, |
| (uint8_t*)&kFrame1); |
| ASSERT_EQ(status, kLibgav1StatusOk); |
| |
| ASSERT_EQ(test.frames_in_use, 0); |
| |
| // Dequeue the output of frame1. |
| status = Libgav1DecoderDequeueFrame(test.decoder, &buffer); |
| ASSERT_EQ(status, kLibgav1StatusOk); |
| ASSERT_NE(buffer, NULL); |
| ASSERT_EQ(test.released_input_buffer, &kFrame1); |
| |
| ASSERT_EQ(test.frames_in_use, 1); |
| |
| // Signal end of stream. |
| status = Libgav1DecoderSignalEOS(test.decoder); |
| |
| // libgav1 should have released all the reference frames now. |
| ASSERT_EQ(test.frames_in_use, 0); |
| |
| // Now, the decoder is ready to accept a new coded video sequence. But, we |
| // try to enqueue a frame that does not have a sequence header (which is not |
| // allowed). |
| |
| // Enqueue frame2 for decoding. |
| status = Libgav1DecoderEnqueueFrame(test.decoder, kFrame2, sizeof(kFrame2), 0, |
| (uint8_t*)&kFrame2); |
| ASSERT_EQ(status, kLibgav1StatusOk); |
| |
| ASSERT_EQ(test.frames_in_use, 0); |
| |
| // Dequeue the output of frame2 (this will fail since no sequence header has |
| // been seen since the last EOS signal). |
| status = Libgav1DecoderDequeueFrame(test.decoder, &buffer); |
| ASSERT_EQ(status, kLibgav1StatusBitstreamError); |
| ASSERT_EQ(test.released_input_buffer, &kFrame2); |
| |
| ASSERT_EQ(test.frames_in_use, 0); |
| |
| Libgav1DecoderDestroy(test.decoder); |
| test.decoder = NULL; |
| } |
| |
| int main(void) { |
| fprintf(stderr, "C DecoderTest started\n"); |
| DecoderTestAPIFlowForNonFrameParallelMode(); |
| DecoderTestNonFrameParallelModeEnqueueMultipleFramesWithoutDequeuing(); |
| DecoderTestNonFrameParallelModeEOSBeforeDequeuingLastFrame(); |
| DecoderTestNonFrameParallelModeInvalidFrameAfterEOS(); |
| fprintf(stderr, "C DecoderTest passed\n"); |
| return 0; |
| } |