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); } } } }
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); } }
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(); }
void MediaPictureResamplerTest::testRescale() { TestData::Fixture* fixture=mFixtures.getFixture("testfile_h264_mp4a_tmcd.mov"); TS_ASSERT(fixture); char filepath[2048]; mFixtures.fillPath(fixture, filepath, sizeof(filepath)); RefPointer<Demuxer> source = Demuxer::make(); source->open(filepath, 0, false, true, 0, 0); int32_t numStreams = source->getNumStreams(); TS_ASSERT_EQUALS(fixture->num_streams, numStreams); int32_t streamToDecode = 0; RefPointer<DemuxerStream> stream = source->getStream(streamToDecode); TS_ASSERT(stream); RefPointer<Decoder> decoder = stream->getDecoder(); TS_ASSERT(decoder); RefPointer<Codec> codec = decoder->getCodec(); TS_ASSERT(codec); TS_ASSERT_EQUALS(Codec::CODEC_ID_H264, codec->getID()); decoder->open(0, 0); TS_ASSERT_EQUALS(PixelFormat::PIX_FMT_YUV420P, decoder->getPixelFormat()); // now, let's start a decoding loop. RefPointer<MediaPacket> packet = MediaPacket::make(); RefPointer<MediaPicture> picture = MediaPicture::make( decoder->getWidth(), decoder->getHeight(), decoder->getPixelFormat()); int32_t rescaleW = decoder->getWidth()/4; int32_t rescaleH = (int32_t)(decoder->getHeight()*5.22); PixelFormat::Type rescaleFmt = PixelFormat::PIX_FMT_RGBA; RefPointer<MediaPicture> rescaled = MediaPicture::make( rescaleW, rescaleH, rescaleFmt); RefPointer<MediaPictureResampler> resampler = MediaPictureResampler::make(rescaled->getWidth(), rescaled->getHeight(), rescaled->getFormat(), picture->getWidth(), picture->getHeight(), picture->getFormat(), 0); resampler->open(); int32_t frameNo = 0; while(source->read(packet.value()) >= 0) { // got a packet; now we try to decode it. if (packet->getStreamIndex() == streamToDecode && packet->isComplete()) { int32_t bytesRead = 0; int32_t byteOffset=0; do { bytesRead = decoder->decodeVideo(picture.value(), packet.value(), byteOffset); if (picture->isComplete()) { writePicture("MediaPictureResamplerTest_testRescaleVideo", &frameNo, picture.value(), resampler.value(), rescaled.value()); } byteOffset += bytesRead; } while(byteOffset < packet->getSize()); // now, handle the case where bytesRead is 0; we need to flush any // cached packets do { decoder->decodeVideo(picture.value(), 0, 0); if (picture->isComplete()) { writePicture("MediaPictureResamplerTest_testRescaleVideo", &frameNo, picture.value(), resampler.value(), rescaled.value()); } } while (picture->isComplete()); } if ((int32_t)(frameNo/30) > 10) // 20 pictures should be enough to see if it's working. break; } source->close(); }
void EncoderTest::testRegression36Internal (const Codec::ID codecId, const int32_t numSamples, const int32_t sampleRate, const int32_t channels, const AudioChannel::Layout channelLayout, const AudioFormat::Type audioFormat, const int64_t bitRate, const char* testOutputName) { VS_LOG_DEBUG("Output filename: %s", testOutputName); RefPointer<Codec> codec = Codec::findEncodingCodec (codecId); RefPointer<Encoder> encoder = Encoder::make (codec.value ()); RefPointer<FilterGraph> graph = FilterGraph::make (); RefPointer<MediaAudio> audio = MediaAudio::make (numSamples, sampleRate, channels, channelLayout, audioFormat); // set the encoder properties we need encoder->setSampleRate (audio->getSampleRate ()); encoder->setSampleFormat (audio->getFormat ()); encoder->setChannelLayout (audio->getChannelLayout ()); encoder->setChannels (audio->getChannels ()); encoder->setProperty ("b", (int64_t) (bitRate)); RefPointer<Rational> tb = Rational::make (1, sampleRate); encoder->setTimeBase (tb.value ()); // create an output muxer RefPointer<Muxer> muxer = Muxer::make (testOutputName, 0, 0); RefPointer<MuxerFormat> format = muxer->getFormat (); if (format->getFlag (MuxerFormat::GLOBAL_HEADER)) encoder->setFlag (Encoder::FLAG_GLOBAL_HEADER, true); // open the encoder encoder->open (0, 0); RefPointer<FilterAudioSink> fsink = graph->addAudioSink ( "out", audio->getSampleRate (), audio->getChannelLayout (), audio->getFormat ()); // Generate a 220 Hz sine wave with a 880 Hz beep each second, for 10 seconds. graph->open ("sine=frequency=220:beep_factor=4:duration=11[out]"); fsink->setFrameSize (numSamples); // add a stream for the encoded packets { RefPointer<MuxerStream> stream = muxer->addNewStream (encoder.value ()); } // and open the muxer muxer->open (0, 0); // now we're (in theory) ready to start writing data. int32_t numCompletePackets = 0; RefPointer<MediaPacket> packet; // Get one audio packet that is larger than the frame-size. fsink->getAudio (audio.value ()); TS_ASSERT(audio->isComplete ()); TS_ASSERT_EQUALS(audio->getNumSamples (), sampleRate); audio->setTimeStamp (0); // let's encode packet = MediaPacket::make (); encoder->encodeAudio (packet.value (), audio.value ()); if (packet->isComplete ()) { muxer->write (packet.value (), false); } // now flush the encoder do { packet = MediaPacket::make (); encoder->encodeAudio (packet.value (), 0); if (packet->isComplete ()) { muxer->write (packet.value (), false); ++numCompletePackets; } } while (packet->isComplete ()); muxer->close (); if (!(codec->getCapabilities() & Codec::CAP_VARIABLE_FRAME_SIZE)) { const int32_t numExpectedPackets = audio->getNumSamples() / encoder->getFrameSize(); VS_LOG_DEBUG("%ld vs %ld; framesize: %ld", numCompletePackets, numExpectedPackets, encoder->getFrameSize()); TS_ASSERT(numCompletePackets > 10); } }
void EncoderTest::testEncodeVideo() { Logger::setGlobalIsLogging(Logger::LEVEL_TRACE, false); LoggerStack stack; stack.setGlobalLevel(Logger::LEVEL_INFO, false); const bool isMemCheck = getenv("VS_TEST_MEMCHECK") ? true : false; const int32_t maxPics = isMemCheck ? 10 : 500; int32_t width=176; int32_t height=144; RefPointer<Codec> codec = Codec::findEncodingCodec(Codec::CODEC_ID_H264); RefPointer<Encoder> encoder = Encoder::make(codec.value()); RefPointer<MediaPicture> picture = MediaPicture::make(width*2,height*2, PixelFormat::PIX_FMT_YUV420P); // set the encoder properties we need encoder->setWidth(picture->getWidth()); encoder->setHeight(picture->getHeight()); encoder->setPixelFormat(picture->getFormat()); encoder->setProperty("b", (int64_t)400000); // bitrate encoder->setProperty("g", (int64_t) 10); // gop encoder->setProperty("bf", (int64_t)1); // max b frames RefPointer<Rational> tb = Rational::make(1,25); encoder->setTimeBase(tb.value()); // mandlebrot, that is then negated, horizontally flipped, and edge detected, before // final outputting to a new picture with each version in one of 4 quadrants. char graphCommand[1024]; snprintf(graphCommand,sizeof(graphCommand),"mandelbrot=s=%dx%d[mb];" "[mb]split=4[0][1][2][3];" "[0]pad=iw*2:ih*2[a];" "[1]negate[b];" "[2]hflip[c];" "[3]edgedetect[d];" "[a][b]overlay=w[x];" "[x][c]overlay=0:h[y];" "[y][d]overlay=w:h[out]", width, height); RefPointer<FilterGraph> graph = FilterGraph::make(); RefPointer<FilterPictureSink> fsink = graph->addPictureSink("out", picture->getFormat()); graph->open(graphCommand); // let's set a frame time base of 1/30 RefPointer<Rational> pictureTb = Rational::make(1,30); // create an output muxer RefPointer<Muxer> muxer = Muxer::make("EncoderTest_encodeVideo.mov", 0, 0); RefPointer<MuxerFormat> format = muxer->getFormat(); // if the container will require a global header, then the encoder needs to set this. if (format->getFlag(MuxerFormat::GLOBAL_HEADER)) encoder->setFlag(Encoder::FLAG_GLOBAL_HEADER, true); // open the encoder encoder->open(0, 0); // add a stream for the encoded packets { RefPointer<MuxerStream> stream = muxer->addNewStream(encoder.value()); } // and open the muxer muxer->open(0, 0); // now we're (in theory) ready to start writing data. int32_t numPics = 0; RefPointer<MediaPacket> packet; while(fsink->getPicture(picture.value()) >= 0 && numPics < maxPics) { picture->setTimeBase(pictureTb.value()); picture->setTimeStamp(numPics); // let's encode packet = MediaPacket::make(); encoder->encodeVideo(packet.value(), picture.value()); if (packet->isComplete()) { muxer->write(packet.value(), false); } ++numPics; } // now flush the encoder do { packet = MediaPacket::make(); encoder->encodeVideo(packet.value(), 0); if (packet->isComplete()) { muxer->write(packet.value(), false); } } while (packet->isComplete()); muxer->close(); }
/** * This test will read ironman (FLV, H263 video and mp3 audio) and transcode * to MP4 (H264 Video and aac audio). */ void EncoderTest::testTranscode() { // enable trace logging Logger::setGlobalIsLogging(Logger::LEVEL_TRACE, false); const bool isMemCheck = getenv("VS_TEST_MEMCHECK") ? true : false; LoggerStack stack; stack.setGlobalLevel(Logger::LEVEL_INFO, false); TestData::Fixture* fixture; fixture=mFixtures.getFixture("testfile.flv"); // fixture=mFixtures.getFixture("testfile_h264_mp4a_tmcd.mov"); // fixture=mFixtures.getFixture("bigbuckbunny_h264_aac_5.1.mp4"); TS_ASSERT(fixture); char filepath[2048]; mFixtures.fillPath(fixture, filepath, sizeof(filepath)); RefPointer<Demuxer> source = Demuxer::make(); source->open(filepath, 0, false, true, 0, 0); int32_t numStreams = source->getNumStreams(); TS_ASSERT_EQUALS(fixture->num_streams, numStreams); // Let's create a helper object to help us with decoding typedef struct { MediaDescriptor::Type type; RefPointer<DemuxerStream> stream; RefPointer<Decoder> decoder; RefPointer<MediaSampled> media; } DemuxerStreamHelper; // I know there are only 2 in the input file. DemuxerStreamHelper inputHelpers[10]; for(int32_t i = 0; i < numStreams; i++) { DemuxerStreamHelper* input = &inputHelpers[i]; input->stream = source->getStream(i); input->decoder = input->stream->getDecoder(); if (!input->decoder) // skip break; input->decoder->open(0, 0); input->type = input->decoder->getCodecType(); if (input->type == MediaDescriptor::MEDIA_AUDIO) input->media = MediaAudio::make( input->decoder->getFrameSize(), input->decoder->getSampleRate(), input->decoder->getChannels(), input->decoder->getChannelLayout(), input->decoder->getSampleFormat()); else if (input->type == MediaDescriptor::MEDIA_VIDEO) input->media = MediaPicture::make( input->decoder->getWidth(), input->decoder->getHeight(), input->decoder->getPixelFormat() ); } // now, let's set up our output file. RefPointer<Muxer> muxer = Muxer::make("EncoderTest_testTranscode.mp4", 0, 0); RefPointer<MuxerFormat> format = muxer->getFormat(); // Let's create a helper object to help us with decoding typedef struct { MediaDescriptor::Type type; RefPointer<MuxerStream> stream; RefPointer<MediaResampler> resampler; RefPointer<MediaSampled> media; RefPointer<Encoder> encoder; } MuxerStreamHelper; MuxerStreamHelper outputHelpers[10]; for(int32_t i = 0; i < numStreams; i++) { DemuxerStreamHelper *input = &inputHelpers[i]; MuxerStreamHelper *output = &outputHelpers[i]; if (!input->decoder) // skip break; output->type = input->type; RefPointer<Encoder> encoder; if (output->type == MediaDescriptor::MEDIA_VIDEO) { RefPointer<Codec> codec = Codec::findEncodingCodec(Codec::CODEC_ID_H264); encoder = Encoder::make(codec.value()); // set the encoder properties we need encoder->setWidth(input->decoder->getWidth()); encoder->setHeight(input->decoder->getHeight()); encoder->setPixelFormat(input->decoder->getPixelFormat()); encoder->setProperty("b", (int64_t)400000); // bitrate encoder->setProperty("g", (int64_t) 10); // gop encoder->setProperty("bf", (int64_t)0); // max b frames RefPointer<Rational> tb = Rational::make(1,2997); encoder->setTimeBase(tb.value()); } else if (output->type == MediaDescriptor::MEDIA_AUDIO) { RefPointer<Codec> codec = Codec::findEncodingCodec(Codec::CODEC_ID_AAC); encoder = Encoder::make(codec.value()); // set the encoder properties we need encoder->setSampleRate(input->decoder->getSampleRate()); encoder->setSampleFormat(input->decoder->getSampleFormat()); encoder->setSampleFormat(AudioFormat::SAMPLE_FMT_S16); encoder->setChannelLayout(input->decoder->getChannelLayout()); encoder->setChannels(input->decoder->getChannels()); encoder->setProperty("b", (int64_t)64000); // bitrate RefPointer<Rational> tb = Rational::make(1,encoder->getSampleRate()); encoder->setTimeBase(tb.value()); // //input->decoder->getTimeBase(); // output->encoder->setTimeBase(tb.value()); } output->encoder.reset(encoder.value(), true); if (output->encoder) { if (format->getFlag(MuxerFormat::GLOBAL_HEADER)) output->encoder->setFlag(Encoder::FLAG_GLOBAL_HEADER, true); output->encoder->open(0,0); // sometimes encoders need to change parameters to fit; let's see // if that happened. output->stream = muxer->addNewStream(output->encoder.value()); } output->media = input->media; output->resampler = 0; if (output->type == MediaDescriptor::MEDIA_AUDIO) { // sometimes encoders only accept certain media types and discard // our suggestions. Let's check. if ( output->encoder->getSampleRate() != input->decoder->getSampleRate() || output->encoder->getSampleFormat() != input->decoder->getSampleFormat() || output->encoder->getChannelLayout() != input->decoder->getChannelLayout() || output->encoder->getChannels() != input->decoder->getChannels() ) { // we need a resampler. VS_LOG_DEBUG("Resampling: [%"PRId32", %"PRId32", %"PRId32"] [%"PRId32", %"PRId32", %"PRId32"]", (int32_t)output->encoder->getChannelLayout(), (int32_t)output->encoder->getSampleRate(), (int32_t)output->encoder->getSampleFormat(), (int32_t)input->decoder->getChannelLayout(), (int32_t)input->decoder->getSampleRate(), (int32_t)input->decoder->getSampleFormat() ); RefPointer<MediaAudioResampler> resampler = MediaAudioResampler::make( output->encoder->getChannelLayout(), output->encoder->getSampleRate(), output->encoder->getSampleFormat(), input->decoder->getChannelLayout(), input->decoder->getSampleRate(), input->decoder->getSampleFormat() ); resampler->open(); output->resampler.reset(resampler.value(), true); output->media = MediaAudio::make( output->encoder->getFrameSize(), output->encoder->getSampleRate(), output->encoder->getChannels(), output->encoder->getChannelLayout(), output->encoder->getSampleFormat()); } } } // now we should be ready to open the muxer muxer->open(0, 0); // now, let's start a decoding loop. RefPointer<MediaPacket> packet = MediaPacket::make(); int numPackets = 0; while(source->read(packet.value()) >= 0) { // got a packet; now we try to decode it. if (packet->isComplete()) { int32_t streamNo = packet->getStreamIndex(); DemuxerStreamHelper *input = &inputHelpers[streamNo]; MuxerStreamHelper* output = &outputHelpers[streamNo]; if (input->decoder) decodeAndEncode( packet.value(), input->decoder.value(), input->media.value(), output->resampler.value(), output->media.value(), muxer.value(), output->encoder.value()); ++numPackets; if (isMemCheck && numPackets > 100) { VS_LOG_WARN("Exiting early under valgrind"); break; } } } // now, flush any cached packets for(int i = 0; i < numStreams; i++) { DemuxerStreamHelper *input = &inputHelpers[i]; MuxerStreamHelper* output = &outputHelpers[i]; if (input->decoder) decodeAndEncode( 0, input->decoder.value(), input->media.value(), output->resampler.value(), output->media.value(), muxer.value(), output->encoder.value()); } source->close(); muxer->close(); }
/** * Test that memory is not leaked/corrupted during error paths. */ void EncoderTest::testEncodeInvalidParameters() { // Sub-test 1 { int32_t width=176; int32_t height=360; // invalid dimensions for H263 codec RefPointer<Codec> codec = Codec::findEncodingCodec(Codec::CODEC_ID_H263); RefPointer<Encoder> encoder = Encoder::make(codec.value()); RefPointer<MediaPicture> picture = MediaPicture::make(width*2,height*2, PixelFormat::PIX_FMT_YUV420P); // set the encoder properties we need encoder->setWidth(picture->getWidth()); encoder->setHeight(picture->getHeight()); encoder->setPixelFormat(picture->getFormat()); encoder->setProperty("b", (int64_t)400000); // bitrate encoder->setProperty("g", (int64_t) 10); // gop RefPointer<Rational> tb = Rational::make(1,25); encoder->setTimeBase(tb.value()); // open the encoder try { // Temporarily turn down logging LoggerStack stack; stack.setGlobalLevel(Logger::LEVEL_ERROR, false); encoder->open(0, 0); TS_FAIL("should never get here"); } catch (std::exception & e) { // ignore exception } } // Sub-test 2 { int32_t width=176; int32_t height=144; RefPointer<Codec> codec = Codec::findEncodingCodec(Codec::CODEC_ID_H264); RefPointer<Encoder> encoder = Encoder::make(codec.value()); RefPointer<MediaPicture> picture = MediaPicture::make(width*2,height*2, PixelFormat::PIX_FMT_YUV420P); // set the encoder properties we need encoder->setWidth(picture->getWidth()); encoder->setHeight(picture->getHeight()); encoder->setPixelFormat(picture->getFormat()); encoder->setProperty("b", (int64_t)400000); // bitrate encoder->setProperty("g", (int64_t) 10); // gop encoder->setProperty("bf", (int64_t)1); // max b frames RefPointer<Rational> tb = Rational::make(1,25); encoder->setTimeBase(tb.value()); // Do not open the encoder // create an output muxer RefPointer<Muxer> muxer = Muxer::make("EncoderTest_encodeVideo.mov", 0, 0); // add a stream for the encoded packets try { // Temporarily turn down logging LoggerStack stack; stack.setGlobalLevel(Logger::LEVEL_ERROR, false); RefPointer<MuxerStream> stream = muxer->addNewStream(encoder.value()); TS_FAIL("should never get here"); } catch (std::exception & e) { // success } } }
void EncoderTest::testEncodeAudio() { Logger::setGlobalIsLogging(Logger::LEVEL_TRACE, false); LoggerStack stack; stack.setGlobalLevel(Logger::LEVEL_INFO, false); const bool isMemCheck = getenv("VS_TEST_MEMCHECK") ? true : false; const int32_t sampleRate = 44100; const int32_t maxSamples = isMemCheck ? sampleRate*0.5 : sampleRate*10; const int32_t numSamples = 1024; const AudioChannel::Layout channelLayout = AudioChannel::CH_LAYOUT_STEREO; const int32_t channels = AudioChannel::getNumChannelsInLayout(channelLayout); const AudioFormat::Type audioFormat = AudioFormat::SAMPLE_FMT_S16; RefPointer<Codec> codec = Codec::findEncodingCodec(Codec::CODEC_ID_AAC); RefPointer<Encoder> encoder = Encoder::make(codec.value()); RefPointer<FilterGraph> graph = FilterGraph::make(); RefPointer<MediaAudio> audio = MediaAudio::make(numSamples, sampleRate, channels, channelLayout, audioFormat); // set the encoder properties we need encoder->setSampleRate(audio->getSampleRate()); encoder->setSampleFormat(audio->getFormat()); encoder->setChannelLayout(audio->getChannelLayout()); encoder->setChannels(audio->getChannels()); encoder->setProperty("b", (int64_t)64000); // bitrate RefPointer<Rational> tb = Rational::make(1,25); encoder->setTimeBase(tb.value()); // create an output muxer RefPointer<Muxer> muxer = Muxer::make("EncoderTest_encodeAudio.mp4", 0, 0); RefPointer<MuxerFormat> format = muxer->getFormat(); if (format->getFlag(MuxerFormat::GLOBAL_HEADER)) encoder->setFlag(Encoder::FLAG_GLOBAL_HEADER, true); // open the encoder encoder->open(0, 0); RefPointer<FilterAudioSink> fsink = graph->addAudioSink("out", audio->getSampleRate(), audio->getChannelLayout(), audio->getFormat()); // Generate a 220 Hz sine wave with a 880 Hz beep each second, for 10 seconds. graph->open("sine=frequency=660:beep_factor=4:duration=11[out]"); // Generate an amplitude modulated signal //graph->open("aevalsrc=sin(10*2*PI*t)*sin(880*2*PI*t)[out]"); // add a stream for the encoded packets { RefPointer<MuxerStream> stream = muxer->addNewStream(encoder.value()); } // and open the muxer muxer->open(0, 0); // now we're (in theory) ready to start writing data. int32_t numFrames = 0; RefPointer<MediaPacket> packet; while(fsink->getAudio(audio.value()) >= 0 && audio->isComplete() && numFrames*audio->getNumSamples() < maxSamples) { audio->setTimeStamp(numFrames*audio->getNumSamples()); // let's encode packet = MediaPacket::make(); encoder->encodeAudio(packet.value(), audio.value()); if (packet->isComplete()) { muxer->write(packet.value(), false); } ++numFrames; } // now flush the encoder do { packet = MediaPacket::make(); encoder->encodeAudio(packet.value(), 0); if (packet->isComplete()) { muxer->write(packet.value(), false); } } while (packet->isComplete()); muxer->close(); }