void MediaAudioTest::testCreationFromBufferPlanar() { const int32_t numSamples = 155; // I choose an odd number because HV will align up to 32 const int32_t sampleRate = 22050; const int32_t channels = 15; // choose a large # of channels to make sure we expand the Frame const AudioChannel::Layout layout = AudioChannel::CH_LAYOUT_UNKNOWN; const AudioFormat::Type format = AudioFormat::SAMPLE_FMT_DBLP; int32_t bufSize = AudioFormat::getBufferSizeNeeded(numSamples, channels, format); // test that there is rounding up int32_t minSize = AudioFormat::getBytesPerSample(format) * numSamples * channels; TS_ASSERT_LESS_THAN(minSize, bufSize); RefPointer<Buffer> src = Buffer::make(0, bufSize); double* srcData = (double*) src->getBytes(0, bufSize); // now, let's go nuts! for (size_t i = 0; i < bufSize / sizeof(double); i++) { srcData[i] = i; } RefPointer<MediaAudio> audio; { LoggerStack stack; stack.setGlobalLevel(Logger::LEVEL_ERROR, false); TS_ASSERT_THROWS(MediaAudio::make(0, numSamples, sampleRate, channels, layout, format), HumbleInvalidArgument); } audio = MediaAudio::make(src.value(), numSamples, sampleRate, channels, layout, format); TS_ASSERT(audio); TS_ASSERT_EQUALS(channels, audio->getNumDataPlanes()); // sigh; time to test each plane. for (int i = 0; i < channels; i++) { RefPointer<Buffer> dst = audio->getData(i); TS_ASSERT(dst); double* dstData = (double*) dst->getBytes(0, dst->getBufferSize()); // the values should be monotonically increasing given how we set them. double last = dstData[0]; for (size_t j = 1; j < dst->getBufferSize() / sizeof(double); j++) { TS_ASSERT_DELTA(last + 1, dstData[j], 0.001); last = dstData[j]; } } }
void MediaAudioTest::testCreationFromBufferPacked() { const int32_t numSamples = 155; // I choose an odd number because HV will align up to 32 const int32_t sampleRate = 22050; const int32_t channels = 8; const AudioChannel::Layout layout = AudioChannel::CH_LAYOUT_7POINT1; const AudioFormat::Type format = AudioFormat::SAMPLE_FMT_DBL; int32_t bufSize = AudioFormat::getBufferSizeNeeded(numSamples, channels, format); RefPointer<Buffer> src = Buffer::make(0, bufSize); double* srcData = (double*) src->getBytes(0, bufSize); // now, let's go nuts! for (size_t i = 0; i < bufSize / sizeof(double); i++) { srcData[i] = i; } RefPointer<MediaAudio> audio; { LoggerStack stack; stack.setGlobalLevel(Logger::LEVEL_ERROR, false); TS_ASSERT_THROWS(MediaAudio::make(0, numSamples, sampleRate, channels, layout, format), HumbleInvalidArgument); } audio = MediaAudio::make(src.value(), numSamples, sampleRate, channels, layout, format); TS_ASSERT(audio); TS_ASSERT_EQUALS(1, audio->getNumDataPlanes()); RefPointer<Buffer> dst = audio->getData(0); TS_ASSERT(dst); double* dstData = (double*) dst->getBytes(0, dst->getBufferSize()); // the test in packed data is simple -- the buffers should be equal! TS_ASSERT_EQUALS(src->getBufferSize(), dst->getBufferSize()); TS_ASSERT_EQUALS(srcData, dstData); }
void MediaAudioTest::testCreation() { const int32_t numSamples = 1024; const int32_t sampleRate = 22050; const int32_t channels = 8; const AudioChannel::Layout layout = AudioChannel::CH_LAYOUT_7POINT1; const AudioFormat::Type format = AudioFormat::SAMPLE_FMT_S16P; // now let's test invalid methods. RefPointer<MediaAudio> audio; { LoggerStack stack; stack.setGlobalLevel(Logger::LEVEL_ERROR, false); TS_ASSERT_THROWS(MediaAudio::make(-1, sampleRate, channels, layout, format), HumbleInvalidArgument); } { LoggerStack stack; stack.setGlobalLevel(Logger::LEVEL_ERROR, false); TS_ASSERT_THROWS(MediaAudio::make(numSamples, -1, channels, layout, format), HumbleInvalidArgument); } { LoggerStack stack; stack.setGlobalLevel(Logger::LEVEL_ERROR, false); TS_ASSERT_THROWS(MediaAudio::make(numSamples, sampleRate, -1, layout, format), HumbleInvalidArgument); } { LoggerStack stack; stack.setGlobalLevel(Logger::LEVEL_ERROR, false); TS_ASSERT_THROWS(MediaAudio::make(numSamples, sampleRate, channels + 1, layout, format), HumbleInvalidArgument); } { LoggerStack stack; stack.setGlobalLevel(Logger::LEVEL_ERROR, false); TS_ASSERT_THROWS(MediaAudio::make(numSamples, sampleRate, channels, layout, AudioFormat::SAMPLE_FMT_NONE), HumbleInvalidArgument); } // And this should be valid audio = MediaAudio::make(numSamples, sampleRate, channels + 1, AudioChannel::CH_LAYOUT_UNKNOWN, format); TS_ASSERT(audio); audio = MediaAudio::make(numSamples, sampleRate, channels, layout, format); TS_ASSERT(audio); // now let's try getting the data RefPointer<Buffer> buf; TS_ASSERT_EQUALS(numSamples, audio->getMaxNumSamples()); TS_ASSERT_EQUALS(numSamples, audio->getNumSamples()); TS_ASSERT(!audio->isComplete()); TS_ASSERT_EQUALS(channels, audio->getChannels()); TS_ASSERT_EQUALS(channels, audio->getNumDataPlanes()); TS_ASSERT_EQUALS(layout, audio->getChannelLayout()); TS_ASSERT_EQUALS(sampleRate, audio->getSampleRate()); TS_ASSERT_EQUALS(format, audio->getFormat()); { LoggerStack stack; stack.setGlobalLevel(Logger::LEVEL_ERROR, false); TS_ASSERT_THROWS( audio->setNumSamples(audio->getMaxNumSamples()+1), HumbleInvalidArgument); TS_ASSERT_THROWS(audio->setNumSamples(-1), HumbleInvalidArgument); TS_ASSERT_THROWS(audio->setNumSamples(0), HumbleInvalidArgument); } audio->setNumSamples(1); TS_ASSERT_EQUALS(1, audio->getNumSamples()); audio->setComplete(true); TS_ASSERT(audio->isComplete()); for (int i = 0; i < channels; i++) { buf = audio->getData(i); TS_ASSERT(buf); TS_ASSERT_EQUALS(audio->getDataPlaneSize(i), audio->getNumSamples()*AudioFormat::getBytesPerSample(audio->getFormat())*(audio->isPlanar()?1:audio->getChannels())); } // now let's try packed audio audio = MediaAudio::make(numSamples, sampleRate, channels, layout, AudioFormat::getPackedSampleFormat(format)); TS_ASSERT(audio); TS_ASSERT_EQUALS(AudioFormat::getPackedSampleFormat(format), audio->getFormat()); TS_ASSERT_EQUALS(channels, audio->getChannels()); TS_ASSERT_EQUALS(1, audio->getNumDataPlanes()); buf = audio->getData(0); TS_ASSERT(buf); for (int i = 1; i < channels; i++) { LoggerStack stack; stack.setGlobalLevel(Logger::LEVEL_ERROR, false); TS_ASSERT_THROWS(audio->getData(i), HumbleInvalidArgument); } }
void MediaAudioTest::testCopy() { const int32_t numSamples = 155; // I choose an odd number because HV will align up to 32 const int32_t sampleRate = 22050; const int32_t channels = 15; // choose a large # of channels to make sure we expand the Frame const AudioChannel::Layout layout = AudioChannel::CH_LAYOUT_UNKNOWN; const AudioFormat::Type format = AudioFormat::SAMPLE_FMT_DBLP; int32_t bufSize = AudioFormat::getBufferSizeNeeded(numSamples, channels, format); // test that there is rounding up int32_t minSize = AudioFormat::getBytesPerSample(format) * numSamples * channels; TS_ASSERT_LESS_THAN(minSize, bufSize); RefPointer<Buffer> src = Buffer::make(0, bufSize); double* srcData = (double*) src->getBytes(0, bufSize); // now, let's go nuts! for (size_t i = 0; i < bufSize / sizeof(double); i++) { srcData[i] = i; } RefPointer<MediaAudio> audio; audio = MediaAudio::make(src.value(), numSamples, sampleRate, channels, layout, format); TS_ASSERT(audio); TS_ASSERT_EQUALS(channels, audio->getNumDataPlanes()); bool tests[] = { true, false }; for (size_t i = 0; i < sizeof(tests) / sizeof(*tests); i++) { // now let's make a copy RefPointer<MediaAudio> copy = MediaAudio::make(audio.value(), tests[i]); TS_ASSERT_EQUALS(copy->getMaxNumSamples(), audio->getMaxNumSamples()); TS_ASSERT_EQUALS(copy->getNumSamples(), audio->getNumSamples()); TS_ASSERT_EQUALS(copy->getChannels(), audio->getChannels()); TS_ASSERT_EQUALS(copy->getNumDataPlanes(), audio->getNumDataPlanes()); TS_ASSERT_EQUALS(copy->getChannelLayout(), audio->getChannelLayout()); TS_ASSERT_EQUALS(copy->getSampleRate(), audio->getSampleRate()); TS_ASSERT_EQUALS(copy->getFormat(), audio->getFormat()); for (int32_t j = 0; j < audio->getNumDataPlanes(); j++) { RefPointer<Buffer> srcBuf = audio->getData(j); RefPointer<Buffer> dstBuf = copy->getData(j); int32_t planeSize = srcBuf->getBufferSize(); TS_ASSERT_EQUALS(planeSize, dstBuf->getBufferSize()); uint8_t* srcBytes = (uint8_t*) srcBuf->getBytes(0, planeSize); uint8_t* dstBytes = (uint8_t*) dstBuf->getBytes(0, planeSize); if (tests[i]) { for (int32_t k = 0; k < planeSize; k++) { // should be byte-by-byte the same TS_ASSERT_EQUALS(srcBytes[k], dstBytes[k]); } } else { TS_ASSERT_EQUALS(srcBytes, dstBytes); } } } }
MediaPictureImpl* MediaPictureImpl::make(Buffer* buffer, int32_t width, int32_t height, PixelFormat::Type format) { if (width <= 0) { VS_THROW(HumbleInvalidArgument("width must be > 0")); } if (height <= 0) { VS_THROW(HumbleInvalidArgument("height must be > 0")); } if (format == PixelFormat::PIX_FMT_NONE) { VS_THROW(HumbleInvalidArgument("pixel format must be specifie")); } if (!buffer) { VS_THROW(HumbleInvalidArgument("must pass non null buffer")); } // let's figure out how big of a buffer we need int32_t bufSize = PixelFormat::getBufferSizeNeeded(width, height, format); if (bufSize < buffer->getBufferSize()) { VS_THROW( HumbleInvalidArgument( "passed in buffer too small to fit requested image parameters")); } RefPointer<MediaPictureImpl> retval = make(); AVFrame* frame = retval->mFrame; frame->width = width; frame->height = height; frame->format = format; // buffer is large enough; let's fill the data pointers uint8_t* data = (uint8_t*) buffer->getBytes(0, bufSize); int32_t imgSize = av_image_fill_arrays(frame->data, frame->linesize, data, (enum AVPixelFormat) frame->format, frame->width, frame->height, 1); if (imgSize != bufSize) { VS_ASSERT(imgSize == bufSize, "these should always be equal"); VS_THROW(HumbleRuntimeError("could not fill image with data")); } // now, set up the reference buffers frame->extended_data = frame->data; for (int32_t i = 0; i < AV_NUM_DATA_POINTERS; i++) { if (frame->data[i]) frame->buf[i] = AVBufferSupport::wrapBuffer(buffer, frame->data[i], frame->linesize[0]*frame->height+16); } // now fill in the AVBufferRefs where we pass of to FFmpeg care // of our buffer. Be kind FFmpeg. Be kind. RefPointer<PixelFormatDescriptor> desc = PixelFormat::getDescriptor((PixelFormat::Type)frame->format); if (!desc) { VS_THROW(HumbleRuntimeError("could not get format descriptor")); } if (desc->getFlag(PixelFormatDescriptor::PIX_FMT_FLAG_PAL) || desc->getFlag(PixelFormatDescriptor::PIX_FMT_FLAG_PSEUDOPAL)) { av_buffer_unref(&frame->buf[1]); frame->buf[1] = AVBufferSupport::wrapBuffer(Buffer::make(retval.value(), 1024)); if (!frame->buf[1]) { VS_THROW(HumbleRuntimeError("memory failure")); } frame->data[1] = frame->buf[1]->data; } int32_t n = retval->getNumDataPlanes(); (void) n; VS_LOG_TRACE("Created MediaPicture: %d x %d (%d). [%d, %d, %d, %d]", retval->getWidth(), retval->getHeight(), retval->getFormat(), n < 1 ? 0 : retval->getDataPlaneSize(0), n < 2 ? 0 : retval->getDataPlaneSize(1), n < 3 ? 0 : retval->getDataPlaneSize(2), n < 4 ? 0 : retval->getDataPlaneSize(3) ); // and we're done. return retval.get(); }