void
BitStreamFilterTest::testNoiseFilter() {
  const char* name = "noise";
  RefPointer<BitStreamFilter> f = BitStreamFilter::make(name);

  // let's generate a buffer to add noise to.
  const int32_t inSize = 1024;
  // Annoyingly I have to add FF_INPUT_BUFFER_PADDING_SIZE because the noise
  // filter incorrectly memcopies an additional 16 bytes, and I want Valgrind to
  // not worry about that.
  RefPointer<Buffer> inBuf = Buffer::make(0, inSize+FF_INPUT_BUFFER_PADDING_SIZE);
  uint8_t* in = static_cast<uint8_t*>(inBuf->getBytes(0, inSize));
  int32_t outSize = inSize;
  RefPointer<Buffer> outBuf = Buffer::make(0, outSize+FF_INPUT_BUFFER_PADDING_SIZE);
  uint8_t* out = static_cast<uint8_t*>(outBuf->getBytes(0, outSize));
  for(int32_t i = 0; i < inSize+FF_INPUT_BUFFER_PADDING_SIZE; i++) {
    in[i] = static_cast<uint8_t>(i);
    out[i] = in[i];
  }

  outSize = f->filter(outBuf.value(), 0, inBuf.value(), 0, inSize, 0, 0, false);
  TS_ASSERT_EQUALS(outSize, inSize);
  int matches = 0;
  for(int32_t i = 0; i < inSize; i++) {
    if (in[i] == out[i])
      ++ matches;
  }
  // noise should make sure that not all elements match.
  TS_ASSERT(matches < inSize);
}
void
BitStreamFilterTest::testChompFilter() {
  const char* name = "chomp";
  RefPointer<BitStreamFilter> f = BitStreamFilter::make(name);

  // let's generate a buffer to chomp nulls off the end of.
  const int32_t inSize = 1024;
  RefPointer<Buffer> inBuf = Buffer::make(0, inSize);
  uint8_t* in = static_cast<uint8_t*>(inBuf->getBytes(0, inSize));
  int32_t outSize = inSize;
  RefPointer<Buffer> outBuf = Buffer::make(0, outSize);
  uint8_t* out = static_cast<uint8_t*>(outBuf->getBytes(0, outSize));

  // set the entire input buffer to null for now.
  memset(in, 0, inSize);
  // then set the first half of the buffer to be non-null.
  for(int32_t i = 0; i < inSize/2; i++) {
    in[i] = 0x01;
    out[i] = in[i];
  }

  // should filter out all the null bytes at the end.
  outSize = f->filter(outBuf.value(), 0, inBuf.value(), 0, inSize, 0, 0, false);
  TS_ASSERT_EQUALS(outSize, inSize/2);
  for(int32_t i = 0; i < inSize/2; i++) {
    TS_ASSERT_EQUALS(in[i], out[i]);
  }
}
void
StreamCoderTest :: testGetSetExtraData()
{
  int retval = 0;
  h->setupReading("testfile_h264_mp4a_tmcd.mov");
  for(int i = 0; i < h->num_streams; i++)
  {
    if (h->coders[i]->getCodecType() != ICodec::CODEC_TYPE_VIDEO)
      continue;
    retval = h->coders[i]->open();
    VS_TUT_ENSURE(0, retval >= 0);
    int32_t extraDataSize = h->coders[i]->getExtraDataSize();
    VS_TUT_ENSURE(0, extraDataSize > 0);
    uint8_t* bytes;
    if (extraDataSize > 0)
    {
      RefPointer<IBuffer> buffer = IBuffer::make(0, extraDataSize);
      retval = h->coders[i]->getExtraData(buffer.value(), 0, extraDataSize);
      VS_TUT_ENSURE_EQUALS(0, retval, extraDataSize);
      bytes = (uint8_t*)buffer->getBytes(0, extraDataSize);
      for(int j = 0; j < extraDataSize; j++)
      {
//        VS_LOG_DEBUG("Byte %d: %hhd", j, bytes[j]);
        bytes[j] = 5;
      }
      retval = h->coders[i]->setExtraData(buffer.value(), 0, extraDataSize, false);
      VS_TUT_ENSURE_EQUALS(0, retval, extraDataSize);
      // reset the buffer
      buffer = IBuffer::make(0, extraDataSize+1);
      retval = h->coders[i]->getExtraData(buffer.value(), 0, extraDataSize);
      VS_TUT_ENSURE_EQUALS(0, retval, extraDataSize);
      bytes = (uint8_t*)buffer->getBytes(0, extraDataSize);
      for(int j = 0; j < extraDataSize; j++)
      {
        VS_TUT_ENSURE_EQUALS(0, bytes[j], 5);
      }
      bytes[extraDataSize] = 5;
      // test the path where we re-alloc buffers
      retval = h->coders[i]->setExtraData(buffer.value(), 0, extraDataSize+1, true);
      VS_TUT_ENSURE_EQUALS(0, retval, extraDataSize+1);
      // reset the buffer
      buffer = IBuffer::make(0, extraDataSize+1);
      retval = h->coders[i]->getExtraData(buffer.value(), 0, extraDataSize+1);
      VS_TUT_ENSURE_EQUALS(0, retval, extraDataSize+1);
      bytes = (uint8_t*)buffer->getBytes(0, extraDataSize+1);
      for(int j = 0; j < extraDataSize+1; j++)
      {
        VS_TUT_ENSURE_EQUALS(0, bytes[j], 5);
      }
      h->coders[i]->close();
    }
  }
}
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
MediaPictureResamplerTest::writePicture(const char* prefix, int32_t* frameNo,
    MediaPicture* picture,
    MediaPictureResampler* resampler,
    MediaPicture* resampled)
{
  char filename[2048];

  // resample the image from YUV to RGBA
  resampler->resample(resampled, picture);

  // write data as PGM file.
  snprintf(filename, sizeof(filename), "%s-%06d.png", prefix, *frameNo);
  // only write every n'th frame to save disk space
  RefPointer<Buffer> buf = resampled->getData(0);
  uint8_t* data = (uint8_t*)buf->getBytes(0, buf->getBufferSize());
  if (!((*frameNo) % 30)) {
    lodepng_encode32_file(filename, data, resampled->getWidth(), resampled->getHeight());
  }
  (*frameNo)++;

  // check the frame metadata
  RefPointer<KeyValueBag> md = picture->getMetaData();
  if (md) {
    int n =  md->getNumKeys();
    for(int i = 0; i < n; i++) {
      const char* key = md->getKey(i);
      const char* val = md->getValue(key, KeyValueBag::KVB_NONE);
      fprintf(stderr, "%s : %s", key, val);
    }
  }

}
void
AudioResamplerTest :: testDifferentResampleRates()
{
  int retval = -1;
  RefPointer<IAudioResampler> sampler;

  RefPointer<IAudioSamples> inSamples = IAudioSamples::make(4096, 1);
  RefPointer<IAudioSamples> outSamples = IAudioSamples::make(4096*4, 1);

  // initialize our starting samples
  RefPointer<IBuffer> inBuffer = inSamples->getData();

  int16_t * inputBuf = (int16_t*)inBuffer->getBytes(0,
      inSamples->getNumSamples());
  VS_TUT_ENSURE("!samples", inputBuf);

  unsigned int tests[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
      11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
      32, 64, 128,
      256, 512, 1024, 2048, 3072, 4096 };
  unsigned int j = 0;
  // New sample every time
  sampler = IAudioResampler::make(2, 1, 44100, 22050);
  VS_TUT_ENSURE("! sampler", sampler);

  unsigned int totalInput = 0;
  unsigned int totalOutput = 0;
  for (j = 0; j < sizeof(tests)/sizeof(unsigned int); j++)
  {
    unsigned int i=0;

    for (i = 0; i < tests[j]; i++)
    {
      // This is a hack and doesn't need real randomness; just tesing.
      bool negative= rand() % 2;
      inputBuf[i] = (int16_t)(32767.0*((double)rand()/(double)(RAND_MAX))) * (negative ? -1 : 1);
    }
    totalInput += tests[j];
    inSamples->setComplete(true, i, 22050, 1,
        IAudioSamples::FMT_S16, 0);
    retval = sampler->resample(outSamples.value(),
        inSamples.value(),
        inSamples->getNumSamples());
    VS_TUT_ENSURE("should succeed", retval >= 0);
    totalOutput += retval;
    VS_LOG_TRACE("i: %d; o: %d; ti: %d; to: %d",
        inSamples->getNumSamples(),
        outSamples->getNumSamples(),
        totalInput,
        totalOutput);
  }
  // That was all so we could test this.  audio_resample hard codes the filter
  // coefficient in the resample to 16 samples, so we should always hold back
  // at least 16.
  VS_TUT_ENSURE_EQUALS("more than I thought", totalOutput, (totalInput)*2-16);

}
void
MediaPacketTest::testWrapBuffer()
{
  const int size = 512;
  RefPointer<Buffer> buf = Buffer::make(0, size);
  uint8_t* d = (uint8_t*)buf->getBytes(0, size);
  for(int i = 0; i < size; i++)
    d[i] = i % 16;

  packet = MediaPacket::make(buf.value());

  RefPointer<Buffer> data = packet->getData();
  TSM_ASSERT_DIFFERS("should be different Buffer objects since once in a packet, AV manages buffer",
      buf.value(), data.value());
  TS_ASSERT_EQUALS(size, data->getBufferSize());
  TS_ASSERT_EQUALS(size, packet->getSize());
  uint8_t* raw = (uint8_t*) data->getBytes(0, size);
  for (int j = 0; j < size; j++)
    TS_ASSERT_EQUALS(raw[j], j % 16);
}
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
MediaPacketTest::testCopyPacket() {
  const int32_t size = 512;
  packet = MediaPacket::make(size);
  TSM_ASSERT("was able to allocate packet", packet);

  // everything else should be garbage.
  int64_t position = packet->getPosition();
  TSM_ASSERT("position was not set to -1", position == -1);

  position = 4;
  packet->setPosition(position);
  int64_t dts = 28349762;
  packet->setDts(dts);
  int64_t pts = 82729373;
  packet->setPts(pts);
  RefPointer<Rational> timeBase = Rational::make(3, 28972);
  packet->setTimeBase(timeBase.value());
  int32_t streamIndex = 8;
  packet->setStreamIndex(streamIndex);
  int64_t duration = 28387728;
  packet->setDuration(duration);
  int64_t convergenceDuration = 283;
  packet->setConvergenceDuration(convergenceDuration);

  // let's get access to the data

  RefPointer<Buffer> data = packet->getData();
  TS_ASSERT_EQUALS(size+16, data->getBufferSize());
  TS_ASSERT_EQUALS(size, packet->getSize());
  uint8_t* raw = (uint8_t*) data->getBytes(0, size);
  for (int i = 0; i < size; i++)
    raw[i] = i % 16;

  // Now, make a copy
  bool tests[] =
    { true, false };
  for (size_t i = 0; i < (sizeof(tests) / sizeof(tests[0])); i++) {
    RefPointer<MediaPacket> newPacket = MediaPacket::make(packet.value(), tests[i]);
    TSM_ASSERT("should not be empty", newPacket);

    // let's make sure that when not copying, the data is the same.

    TSM_ASSERT_EQUALS("should equal", position, newPacket->getPosition());
    TSM_ASSERT_EQUALS("should equal", pts, newPacket->getPts());
    TSM_ASSERT_EQUALS("should equal", dts, newPacket->getDts());
    TSM_ASSERT_EQUALS("should equal", streamIndex, newPacket->getStreamIndex());
    TSM_ASSERT_EQUALS("should equal", duration, newPacket->getDuration());
    TSM_ASSERT_EQUALS("should equal", convergenceDuration,
        newPacket->getConvergenceDuration());
    RefPointer<Rational> newBase = newPacket->getTimeBase();
    TSM_ASSERT("should be equal", newBase->compareTo(timeBase.value()) == 0);

    RefPointer<Buffer> buf = newPacket->getData();
    TS_ASSERT_EQUALS(size, newPacket->getSize());
    TS_ASSERT_EQUALS(size+16, buf->getBufferSize());
    uint8_t* d = (uint8_t*) buf->getBytes(0, size);
    if (!tests[i]) {
      TS_ASSERT_EQUALS(d, raw);
    } else {
      TS_ASSERT_DIFFERS(d, raw);
    }

    TS_ASSERT(d);
    for (int j = 0; j < size; j++) {
      TS_ASSERT_EQUALS(d[j], j % 16);
    }
  }
}
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
AudioResamplerTest :: testTimeStampIsAdjustedWhenResamplerEatsBytesUpsampling()
{
  int retval = -1;
  int numSamples = 10000;
  int iSampleRate = numSamples;
  int oSampleRate = iSampleRate*2;
  RefPointer<IAudioResampler> sampler;

  RefPointer<IAudioSamples> inSamples = IAudioSamples::make(numSamples, 1);
  RefPointer<IAudioSamples> outSamples = IAudioSamples::make(numSamples*oSampleRate/iSampleRate, 2);

  // initialize our starting samples
  RefPointer<IBuffer> inBuffer = inSamples->getData();
  int16_t * inputBuf = (int16_t*)inBuffer->getBytes(0,
      inSamples->getNumSamples());
  VS_TUT_ENSURE("!samples", inputBuf);

  sampler = IAudioResampler::make(2, 1, oSampleRate, iSampleRate);
  VS_TUT_ENSURE("! sampler", sampler);

  for (int i = 0; i < numSamples; i++)
  {
    // This is a hack and doesn't need real randomness; just testing.
    bool negative= rand() % 2;
    inputBuf[i] = (int16_t)(32767.0*((double)rand()/(double)(RAND_MAX))) * (negative ? -1 : 1);
  }
  // Do it once...
  inSamples->setComplete(true, numSamples, iSampleRate, 1,
      IAudioSamples::FMT_S16, 0);
  retval = sampler->resample(outSamples.value(),
      inSamples.value(),
      inSamples->getNumSamples());
  VS_TUT_ENSURE("should succeed", retval >= 0);
  VS_LOG_TRACE("r: %d; i: %d; o: %d; ipts: %lld; opts: %lld",
      retval,
      inSamples->getNumSamples(),
      outSamples->getNumSamples(),
      inSamples->getPts(),
      outSamples->getPts());
  VS_TUT_ENSURE_EQUALS("should eat exactly 16 samples",
      outSamples->getNumSamples(),
      inSamples->getNumSamples()*oSampleRate/iSampleRate - 16);

  // Do it twice
  inSamples->setComplete(true, numSamples, iSampleRate, 1,
      IAudioSamples::FMT_S16, 1*Global::DEFAULT_PTS_PER_SECOND);
  retval = sampler->resample(outSamples.value(),
      inSamples.value(),
      inSamples->getNumSamples());
  VS_TUT_ENSURE("should succeed", retval >= 0);
  VS_LOG_TRACE("r: %d; i: %d; o: %d; ipts: %lld; opts: %lld",
      retval,
      inSamples->getNumSamples(),
      outSamples->getNumSamples(),
      inSamples->getPts(),
      outSamples->getPts());
  VS_TUT_ENSURE_EQUALS("should eat no samples",
      outSamples->getNumSamples(),
      inSamples->getNumSamples()*oSampleRate/iSampleRate);
  VS_TUT_ENSURE_EQUALS("timestamp should be back 16 samples worth",
      outSamples->getPts(),
      inSamples->getPts() - IAudioSamples::samplesToDefaultPts(16, oSampleRate));

  // Do it thrice
  inSamples->setComplete(true, numSamples, iSampleRate, 1,
      IAudioSamples::FMT_S16, 2*Global::DEFAULT_PTS_PER_SECOND);
  retval = sampler->resample(outSamples.value(),
      inSamples.value(),
      inSamples->getNumSamples());
  VS_TUT_ENSURE("should succeed", retval >= 0);
  VS_LOG_TRACE("r: %d; i: %d; o: %d; ipts: %lld; opts: %lld",
      retval,
      inSamples->getNumSamples(),
      outSamples->getNumSamples(),
      inSamples->getPts(),
      outSamples->getPts());
  VS_TUT_ENSURE_EQUALS("should eat no samples",
      outSamples->getNumSamples(),
      inSamples->getNumSamples()*oSampleRate/iSampleRate);
  VS_TUT_ENSURE_EQUALS("timestamp should be back 16 samples worth",
        outSamples->getPts(),
        inSamples->getPts() - IAudioSamples::samplesToDefaultPts(16, oSampleRate));

  // get the trailing bytes
  retval = sampler->resample(outSamples.value(),
      0,
      0);
  VS_TUT_ENSURE("should succeed", retval >= 0);
  VS_LOG_TRACE("r: %d; i: %d; o: %d; ipts: %lld; opts: %lld",
      retval,
      0,
      outSamples->getNumSamples(),
      0LL,
      outSamples->getPts());
  VS_TUT_ENSURE_EQUALS("timestamp should be back 16 samples worth from end of last samples given",
        outSamples->getPts(),
        inSamples->getNextPts() - IAudioSamples::samplesToDefaultPts(16, oSampleRate));

}