void StreamCoderTest :: testDecodingAndEncodingFullyInterleavedFile() { RefPointer<IAudioSamples> samples = 0; RefPointer<IVideoPicture> frame = 0; RefPointer<IRational> num(0); RefPointer<IStreamCoder> ic(0); RefPointer<IStreamCoder> oc(0); int numSamples = 0; int numPackets = 0; int numFrames = 0; int numKeyFrames = 0; int retval = -1; h->setupReading(h->SAMPLE_FILE); RefPointer<IPacket> packet = IPacket::make(); hw->setupWriting("StreamCoderTest_6_output.mov", "mpeg4", "libmp3lame", "mov"); RefPointer<IPacket> opacket = IPacket::make(); VS_TUT_ENSURE("! opacket", opacket); RefPointer<IMetaData> inputProps = IMetaData::make(); RefPointer<IMetaData> outputProps = IMetaData::make(); { // Let's set up audio first. ic = h->coders[h->first_input_audio_stream]; oc = hw->coders[hw->first_output_audio_stream]; // Set the output coder correctly. oc->setSampleRate(ic->getSampleRate()); oc->setChannels(ic->getChannels()); // Using the MetaData structure char tmpBuf[1024]; snprintf(tmpBuf, sizeof(tmpBuf), "%d", ic->getBitRate()); inputProps->setValue("b", tmpBuf); const char* invalidKey="kd82jclkjdi2oc001,ss2-948"; const char* invalidValue="c9xu1nxL28fJ9"; inputProps->setValue(invalidKey, invalidValue); samples = IAudioSamples::make(1024, ic->getChannels()); VS_TUT_ENSURE("got no samples", samples); retval = ic->open(0,0); VS_TUT_ENSURE("Could not open input coder", retval >= 0); retval = oc->open(inputProps.value(), outputProps.value()); VS_TUT_ENSURE("Could not open output coder", retval >= 0); // now let's ensure our fake property was not set. VS_TUT_ENSURE("Should only have one unset setting", outputProps->getNumKeys() == 1) VS_TUT_ENSURE("Should have expected value", strcmp(outputProps->getValue(invalidKey, IMetaData::METADATA_NONE), invalidValue)==0); } { // now, let's set up video. ic = h->coders[h->first_input_video_stream]; oc = hw->coders[hw->first_output_video_stream]; // We're going to set up high quality video oc->setBitRate(720000); oc->setGlobalQuality(0); oc->setFlags(oc->getFlags() | IStreamCoder::FLAG_QSCALE); oc->setPixelType(ic->getPixelType()); oc->setHeight(ic->getHeight()); oc->setWidth(ic->getWidth()); num = IRational::make(1,15); oc->setFrameRate(num.value()); num = ic->getTimeBase(); oc->setTimeBase(num.value()); oc->setNumPicturesInGroupOfPictures( ic->getNumPicturesInGroupOfPictures() ); // Ask the StreamCoder to guess width and height for us frame = IVideoPicture::make( ic->getPixelType(), -1, -1); VS_TUT_ENSURE("got no frame", frame); VS_TUT_ENSURE("should not have width", frame->getWidth() <= 0); VS_TUT_ENSURE("should not have height", frame->getHeight() <= 0); retval = ic->open(); VS_TUT_ENSURE("Could not open input coder", retval >= 0); retval = oc->open(); VS_TUT_ENSURE("Could not open output coder", retval >= 0); } // write header retval = hw->container->writeHeader(); VS_TUT_ENSURE("could not write header", retval >= 0); while (h->container->readNextPacket(packet.value()) == 0) { ic = h->coders[packet->getStreamIndex()]; oc = hw->coders[packet->getStreamIndex()]; if (packet->getStreamIndex() == h->first_input_audio_stream) { int offset = 0; numPackets++; while (offset < packet->getSize()) { retval = ic->decodeAudio( samples.value(), packet.value(), offset); VS_TUT_ENSURE("could not decode any audio", retval > 0); offset += retval; VS_TUT_ENSURE("could not write any samples", samples->getNumSamples() > 0); numSamples += samples->getNumSamples(); // now, write out the packets. unsigned int numSamplesConsumed = 0; do { retval = oc->encodeAudio(opacket.value(), samples.value(), numSamplesConsumed); VS_TUT_ENSURE("Could not encode any audio", retval > 0); numSamplesConsumed += retval; if (opacket->isComplete()) { VS_TUT_ENSURE("audio duration not set", opacket->getDuration() > 0); VS_TUT_ENSURE("could not encode audio", opacket->getSize() > 0); RefPointer<IBuffer> encodedBuffer = opacket->getData(); VS_TUT_ENSURE("no encoded data", encodedBuffer); VS_TUT_ENSURE("less data than there should be", encodedBuffer->getBufferSize() >= opacket->getSize()); retval = hw->container->writePacket(opacket.value()); VS_TUT_ENSURE("could not write packet", retval >= 0); } } while (numSamplesConsumed < samples->getNumSamples()); } } else if (packet->getStreamIndex() == h->first_input_video_stream) { int offset = 0; numPackets++; VS_LOG_TRACE("packet: pts: %ld; dts: %ld", packet->getPts(), packet->getDts()); while (offset < packet->getSize()) { retval = ic->decodeVideo( frame.value(), packet.value(), offset); VS_TUT_ENSURE("could not decode any video", retval>0); num = ic->getTimeBase(); VS_TUT_ENSURE_EQUALS("time base changed", num->getDouble(), h->expected_time_base); offset += retval; if (frame->isComplete()) { VS_TUT_ENSURE("should now have a frame width", frame->getWidth() > 0); VS_TUT_ENSURE("should now have a frame height", frame->getHeight() > 0); numFrames++; if (frame->isKeyFrame()) numKeyFrames++; frame->setQuality(0); retval = oc->encodeVideo( opacket.value(), frame.value(), -1); VS_TUT_ENSURE("could not encode video", retval >= 0); VS_TUT_ENSURE("could not encode video", opacket->isComplete()); VS_TUT_ENSURE("could not encode video", opacket->getSize() > 0); RefPointer<IBuffer> encodedBuffer = opacket->getData(); VS_TUT_ENSURE("no encoded data", encodedBuffer); VS_TUT_ENSURE("less data than there should be", encodedBuffer->getBufferSize() >= opacket->getSize()); // now, write the packet to disk. retval = hw->container->writePacket(opacket.value()); VS_TUT_ENSURE("could not write packet", retval >= 0); } } } } // sigh; it turns out that to flush the encoding buffers you need to // ask the encoder to encode a NULL set of samples. So, let's do that. retval = hw->coders[hw->first_output_audio_stream]->encodeAudio(opacket.value(), 0, 0); VS_TUT_ENSURE("Could not encode any audio", retval >= 0); if (opacket->isComplete()) { retval = hw->container->writePacket(opacket.value()); VS_TUT_ENSURE("could not write packet", retval >= 0); } retval = hw->coders[hw->first_output_video_stream]->encodeVideo(opacket.value(), 0, 0); VS_TUT_ENSURE("Could not encode any video", retval >= 0); if (opacket->isComplete()) { retval = hw->container->writePacket(opacket.value()); VS_TUT_ENSURE("could not write packet", retval >= 0); } retval = hw->container->writeTrailer(); VS_TUT_ENSURE("! writeTrailer", retval >= 0); retval = h->coders[h->first_input_audio_stream]->close(); VS_TUT_ENSURE("! close", retval >= 0); retval = h->coders[h->first_input_video_stream]->close(); VS_TUT_ENSURE("! close", retval >= 0); retval = hw->coders[hw->first_output_audio_stream]->close(); VS_TUT_ENSURE("! close", retval >= 0); retval = hw->coders[hw->first_output_video_stream]->close(); VS_TUT_ENSURE("! close", retval >= 0); VS_TUT_ENSURE("could not get any audio packets", numPackets > 0); VS_TUT_ENSURE("could not decode any audio", numSamples > 0); VS_TUT_ENSURE("could not decode any video", numFrames >0); VS_TUT_ENSURE("could not find any key frames", numKeyFrames >0); if (h->expected_packets) VS_TUT_ENSURE_EQUALS("unexpected number of packets", numPackets, h->expected_packets); if (h->expected_audio_samples) VS_TUT_ENSURE_EQUALS("unexpected number of audio samples", numSamples, h->expected_audio_samples); if (h->expected_video_frames) VS_TUT_ENSURE_EQUALS("unexpected number of video frames", numFrames, h->expected_video_frames); if (h->expected_video_key_frames) VS_TUT_ENSURE_EQUALS("unexpected number of video key frames", numKeyFrames, h->expected_video_key_frames); }
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 StreamCoderTest :: disabled_testDecodingAndEncodingNellymoserAudio() { RefPointer<IAudioSamples> samples = 0; RefPointer<IVideoPicture> frame = 0; RefPointer<IRational> num(0); RefPointer<IStreamCoder> ic(0); RefPointer<IStreamCoder> oc(0); int numSamples = 0; int numPackets = 0; int numFrames = 0; int numKeyFrames = 0; int retval = -1; h->setupReading(h->SAMPLE_FILE); RefPointer<IPacket> packet = IPacket::make(); hw->setupWriting("StreamCoderTest_8_output.flv", "flv", "nellymoser", "flv"); RefPointer<IPacket> opacket = IPacket::make(); VS_TUT_ENSURE("! opacket", opacket); { // Let's set up audio first. ic = h->coders[h->first_input_audio_stream]; oc = hw->coders[hw->first_output_audio_stream]; // Set the output coder correctly. oc->setSampleRate(ic->getSampleRate()); oc->setChannels(ic->getChannels()); oc->setBitRate(ic->getBitRate()); samples = IAudioSamples::make(1024, ic->getChannels()); VS_TUT_ENSURE("got no samples", samples); retval = ic->open(); VS_TUT_ENSURE("Could not open input coder", retval >= 0); retval = oc->open(); VS_TUT_ENSURE("Could not open output coder", retval >= 0); } { // now, let's set up video. ic = h->coders[h->first_input_video_stream]; oc = hw->coders[hw->first_output_video_stream]; // We're going to set up high quality video oc->setBitRate(720000); oc->setGlobalQuality(0); oc->setFlags(oc->getFlags() | IStreamCoder::FLAG_QSCALE); oc->setPixelType(ic->getPixelType()); oc->setHeight(ic->getHeight()); oc->setWidth(ic->getWidth()); num = IRational::make(1,15); oc->setFrameRate(num.value()); num = ic->getTimeBase(); oc->setTimeBase(num.value()); oc->setNumPicturesInGroupOfPictures( ic->getNumPicturesInGroupOfPictures() ); // Ask the StreamCoder to guess width and height for us frame = IVideoPicture::make( ic->getPixelType(), -1, -1); VS_TUT_ENSURE("got no frame", frame); VS_TUT_ENSURE("should not have width", frame->getWidth() <= 0); VS_TUT_ENSURE("should not have height", frame->getHeight() <= 0); retval = ic->open(); VS_TUT_ENSURE("Could not open input coder", retval >= 0); retval = oc->open(); VS_TUT_ENSURE("Could not open output coder", retval >= 0); } // write header retval = hw->container->writeHeader(); VS_TUT_ENSURE("could not write header", retval >= 0); while (h->container->readNextPacket(packet.value()) == 0) { ic = h->coders[packet->getStreamIndex()]; oc = hw->coders[packet->getStreamIndex()]; if (packet->getStreamIndex() == h->first_input_audio_stream) { int offset = 0; numPackets++; while (offset < packet->getSize()) { retval = ic->decodeAudio( samples.value(), packet.value(), offset); VS_TUT_ENSURE("could not decode any audio", retval > 0); offset += retval; VS_TUT_ENSURE("could not write any samples", samples->getNumSamples() > 0); numSamples += samples->getNumSamples(); // now, write out the packets. unsigned int numSamplesConsumed = 0; do { retval = oc->encodeAudio(opacket.value(), samples.value(), numSamplesConsumed); VS_TUT_ENSURE("Could not encode any audio", retval > 0); numSamplesConsumed += retval; if (opacket->isComplete()) { VS_TUT_ENSURE("could not encode audio", opacket->getSize() > 0); RefPointer<IBuffer> encodedBuffer = opacket->getData(); VS_TUT_ENSURE("no encoded data", encodedBuffer); VS_TUT_ENSURE("less data than there should be", encodedBuffer->getBufferSize() >= opacket->getSize()); retval = hw->container->writePacket(opacket.value()); VS_TUT_ENSURE("could not write packet", retval >= 0); } } while (numSamplesConsumed < samples->getNumSamples()); } } else if (packet->getStreamIndex() == h->first_input_video_stream) { int offset = 0; numPackets++; VS_LOG_TRACE("packet: pts: %ld; dts: %ld", packet->getPts(), packet->getDts()); while (offset < packet->getSize()) { retval = ic->decodeVideo( frame.value(), packet.value(), offset); VS_TUT_ENSURE("could not decode any video", retval>0); num = ic->getTimeBase(); VS_TUT_ENSURE_EQUALS("time base changed", num->getDouble(), h->expected_time_base); offset += retval; if (frame->isComplete()) { VS_TUT_ENSURE("should now have a frame width", frame->getWidth() > 0); VS_TUT_ENSURE("should now have a frame height", frame->getHeight() > 0); numFrames++; if (frame->isKeyFrame()) numKeyFrames++; frame->setQuality(0); retval = oc->encodeVideo( opacket.value(), frame.value(), -1); VS_TUT_ENSURE("could not encode video", retval >= 0); VS_TUT_ENSURE("could not encode video", opacket->isComplete()); VS_TUT_ENSURE("could not encode video", opacket->getSize() > 0); RefPointer<IBuffer> encodedBuffer = opacket->getData(); VS_TUT_ENSURE("no encoded data", encodedBuffer); VS_TUT_ENSURE("less data than there should be", encodedBuffer->getBufferSize() >= opacket->getSize()); // now, write the packet to disk. retval = hw->container->writePacket(opacket.value()); VS_TUT_ENSURE("could not write packet", retval >= 0); } } } } // sigh; it turns out that to flush the encoding buffers you need to // ask the encoder to encode a NULL set of samples. So, let's do that. retval = hw->coders[hw->first_output_audio_stream]->encodeAudio(opacket.value(), 0, 0); VS_TUT_ENSURE("Could not encode any audio", retval >= 0); if (opacket->isComplete()) { retval = hw->container->writePacket(opacket.value()); VS_TUT_ENSURE("could not write packet", retval >= 0); } retval = hw->coders[hw->first_output_video_stream]->encodeVideo(opacket.value(), 0, 0); VS_TUT_ENSURE("Could not encode any video", retval >= 0); if (opacket->isComplete()) { retval = hw->container->writePacket(opacket.value()); VS_TUT_ENSURE("could not write packet", retval >= 0); } retval = hw->container->writeTrailer(); VS_TUT_ENSURE("! writeTrailer", retval >= 0); retval = h->coders[h->first_input_audio_stream]->close(); VS_TUT_ENSURE("! close", retval >= 0); retval = h->coders[h->first_input_video_stream]->close(); VS_TUT_ENSURE("! close", retval >= 0); retval = hw->coders[hw->first_output_audio_stream]->close(); VS_TUT_ENSURE("! close", retval >= 0); retval = hw->coders[hw->first_output_video_stream]->close(); VS_TUT_ENSURE("! close", retval >= 0); VS_TUT_ENSURE("could not get any audio packets", numPackets > 0); VS_TUT_ENSURE("could not decode any audio", numSamples > 0); VS_TUT_ENSURE("could not decode any video", numFrames >0); VS_TUT_ENSURE("could not find any key frames", numKeyFrames >0); if (h->expected_packets) VS_TUT_ENSURE_EQUALS("unexpected number of packets", numPackets, h->expected_packets); if (h->expected_audio_samples) VS_TUT_ENSURE_EQUALS("unexpected number of audio samples", numSamples, h->expected_audio_samples); if (h->expected_video_frames) VS_TUT_ENSURE_EQUALS("unexpected number of video frames", numFrames, h->expected_video_frames); if (h->expected_video_key_frames) VS_TUT_ENSURE_EQUALS("unexpected number of video key frames", numKeyFrames, h->expected_video_key_frames); }
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(); }
void StreamCoderTest :: testTimestamps() { LoggerStack stack; stack.setGlobalLevel(Logger::LEVEL_WARN, false); Helper hr; hr.setupReading("testfile_h264_mp4a_tmcd.mov"); RefPointer<IStreamCoder> ac; RefPointer<IStreamCoder> vc; if (hr.first_input_audio_stream >= 0) ac = hr.coders[hr.first_input_audio_stream]; if (hr.first_input_video_stream >= 0) vc = hr.coders[hr.first_input_video_stream]; hw->setupWriting("StreamCoderTest_7_output.flv", vc ? "flv" : 0, ac ? "libmp3lame" : 0, "flv", vc ? vc->getPixelType() : IPixelFormat::YUV420P, vc ? vc->getWidth() : 0, vc ? vc->getHeight() : 0, false, ac ? ac->getSampleRate() : 0, ac ? ac->getChannels() : 0, true); RefPointer<IVideoPicture> frame; if (vc) { frame = IVideoPicture::make( vc->getPixelType(), vc->getWidth(), vc->getHeight()); } RefPointer<IAudioSamples> samples; if (ac) { samples = IAudioSamples::make(10000, ac->getChannels()); } int retval = -1; int numReads = 0; int maxReads = 10000; while ((retval = hr.readNextDecodedObject(samples.value(), frame.value())) > 0 && numReads < maxReads) { int result = retval; numReads++; VS_TUT_ENSURE("should only return 1 or 2", retval == 1 || retval == 2); if (retval == 1) { VS_TUT_ENSURE("something should be complete...", samples->isComplete()); retval = hw->writeSamples(samples.value()); VS_TUT_ENSURE("could not write audio", retval >= 0); } if (retval == 2) { VS_TUT_ENSURE("something should be complete...", frame->isComplete()); retval = hw->writeFrame(frame.value()); VS_TUT_ENSURE("could not write video", retval >= 0); } { RefPointer<IRational> timebase = hr.coders[hr.packet->getStreamIndex()]->getTimeBase(); ICodec::Type type = hr.coders[hr.packet->getStreamIndex()]->getCodecType(); (void) type; (void) result; VS_LOG_TRACE("Packet stream: %d.%d; pts: %lld; dts: %lld; spts: %lld; fpts: %lld; tb: %d/%d; ret: %d," "samps: %d", hr.packet->getStreamIndex(), type, hr.packet->getPts(), hr.packet->getDts(), samples ? samples->getPts() : -1, frame ? frame->getPts() : -1, timebase->getNumerator(), timebase->getDenominator(), result, samples ? samples->getNumSamples() : 0); } } VS_TUT_ENSURE("should return some data", numReads > 0); }