status_t CopyBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer, int64_t pts) { //ALOGV("CopyBufferProvider(%p)::getNextBuffer(%p (%zu), %lld)", // this, pBuffer, pBuffer->frameCount, pts); if (mLocalBufferFrameCount == 0) { status_t res = mTrackBufferProvider->getNextBuffer(pBuffer, pts); if (res == OK) { copyFrames(pBuffer->raw, pBuffer->raw, pBuffer->frameCount); } return res; } if (mBuffer.frameCount == 0) { mBuffer.frameCount = pBuffer->frameCount; status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts); // At one time an upstream buffer provider had // res == OK and mBuffer.frameCount == 0, doesn't seem to happen now 7/18/2014. // // By API spec, if res != OK, then mBuffer.frameCount == 0. // but there may be improper implementations. ALOG_ASSERT(res == OK || mBuffer.frameCount == 0); if (res != OK || mBuffer.frameCount == 0) { // not needed by API spec, but to be safe. pBuffer->raw = NULL; pBuffer->frameCount = 0; return res; } mConsumed = 0; } ALOG_ASSERT(mConsumed < mBuffer.frameCount); size_t count = min(mLocalBufferFrameCount, mBuffer.frameCount - mConsumed); count = min(count, pBuffer->frameCount); pBuffer->raw = mLocalBufferData; pBuffer->frameCount = count; copyFrames(pBuffer->raw, (uint8_t*)mBuffer.raw + mConsumed * mInputFrameSize, pBuffer->frameCount); return OK; }
SINT SoundSourceMediaFoundation::readSampleFrames( SINT numberOfFrames, CSAMPLE* sampleBuffer) { if (sDebug) { qDebug() << "read()" << numberOfFrames; } SINT framesNeeded(numberOfFrames); // first, copy frames from leftover buffer IF the leftover buffer is at // the correct frame if (m_leftoverBufferLength > 0 && m_leftoverBufferPosition == m_nextFrame) { copyFrames(sampleBuffer, &framesNeeded, m_leftoverBuffer, m_leftoverBufferLength); if (m_leftoverBufferLength > 0) { if (framesNeeded != 0) { qWarning() << __FILE__ << __LINE__ << "WARNING: Expected frames needed to be 0. Abandoning this file."; m_dead = true; } m_leftoverBufferPosition += numberOfFrames; } } else { // leftoverBuffer already empty or in the wrong position, clear it m_leftoverBufferLength = 0; } while (!m_dead && framesNeeded > 0) { HRESULT hr(S_OK); DWORD dwFlags(0); qint64 timestamp(0); IMFSample *pSample(nullptr); bool error(false); // set to true to break after releasing hr = m_pReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, // [in] DWORD dwStreamIndex, 0, // [in] DWORD dwControlFlags, nullptr, // [out] DWORD *pdwActualStreamIndex, &dwFlags, // [out] DWORD *pdwStreamFlags, ×tamp, // [out] LONGLONG *pllTimestamp, &pSample); // [out] IMFSample **ppSample if (FAILED(hr)) { qWarning() << "ReadSample failed!"; break; // abort } if (sDebug) { qDebug() << "ReadSample timestamp:" << timestamp << "frame:" << frameFromMF(timestamp, getSamplingRate()) << "dwflags:" << dwFlags; } if (dwFlags & MF_SOURCE_READERF_ERROR) { // our source reader is now dead, according to the docs qWarning() << "SSMF: ReadSample set ERROR, SourceReader is now dead"; m_dead = true; break; } else if (dwFlags & MF_SOURCE_READERF_ENDOFSTREAM) { qDebug() << "SSMF: End of input file."; break; } else if (dwFlags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED) { qWarning() << "SSMF: Type change"; break; } else if (pSample == nullptr) { // generally this will happen when dwFlags contains ENDOFSTREAM, // so it'll be caught before now -bkgood qWarning() << "SSMF: No sample"; continue; } // we now own a ref to the instance at pSample IMFMediaBuffer *pMBuffer(nullptr); // I know this does at least a memcopy and maybe a malloc, if we have // xrun issues with this we might want to look into using // IMFSample::GetBufferByIndex (although MS doesn't recommend this) if (FAILED(hr = pSample->ConvertToContiguousBuffer(&pMBuffer))) { error = true; goto releaseSample; } CSAMPLE *buffer(nullptr); DWORD bufferLengthInBytes(0); hr = pMBuffer->Lock(reinterpret_cast<quint8**>(&buffer), nullptr, &bufferLengthInBytes); if (FAILED(hr)) { error = true; goto releaseMBuffer; } SINT bufferLength = samples2frames(bufferLengthInBytes / sizeof(buffer[0])); if (m_seeking) { qint64 bufferPosition(frameFromMF(timestamp, getSamplingRate())); if (sDebug) { qDebug() << "While seeking to " << m_nextFrame << "WMF put us at" << bufferPosition; } if (m_nextFrame < bufferPosition) { // Uh oh. We are farther forward than our seek target. Emit // silence? We can't seek backwards here. CSAMPLE* pBufferCurpos = sampleBuffer + frames2samples(numberOfFrames - framesNeeded); qint64 offshootFrames = bufferPosition - m_nextFrame; // If we can correct this immediately, write zeros and adjust // m_nextFrame to pretend it never happened. if (offshootFrames <= framesNeeded) { qWarning() << __FILE__ << __LINE__ << "Working around inaccurate seeking. Writing silence for" << offshootFrames << "frames"; // Set offshootFrames samples to zero. memset(pBufferCurpos, 0, sizeof(*pBufferCurpos) * frames2samples(offshootFrames)); // Now m_nextFrame == bufferPosition m_nextFrame += offshootFrames; framesNeeded -= offshootFrames; } else { // It's more complicated. The buffer we have just decoded is // more than framesNeeded frames away from us. It's too hard // for us to handle this correctly currently, so let's just // try to get on with our lives. m_seeking = false; m_nextFrame = bufferPosition; qWarning() << __FILE__ << __LINE__ << "Seek offshoot is too drastic. Cutting losses and pretending the current decoded audio buffer is the right seek point."; } } if (m_nextFrame >= bufferPosition && m_nextFrame < bufferPosition + bufferLength) { // m_nextFrame is in this buffer. buffer += frames2samples(m_nextFrame - bufferPosition); bufferLength -= m_nextFrame - bufferPosition; m_seeking = false; } else { // we need to keep going forward goto releaseRawBuffer; } } // If the bufferLength is larger than the leftover buffer, re-allocate // it with 2x the space. if (frames2samples(bufferLength) > m_leftoverBufferSize) { SINT newSize = m_leftoverBufferSize; while (newSize < frames2samples(bufferLength)) { newSize *= 2; } CSAMPLE* newBuffer = new CSAMPLE[newSize]; memcpy(newBuffer, m_leftoverBuffer, sizeof(m_leftoverBuffer[0]) * m_leftoverBufferSize); delete[] m_leftoverBuffer; m_leftoverBuffer = newBuffer; m_leftoverBufferSize = newSize; } copyFrames( sampleBuffer + frames2samples(numberOfFrames - framesNeeded), &framesNeeded, buffer, bufferLength); releaseRawBuffer: hr = pMBuffer->Unlock(); // I'm ignoring this, MSDN for IMFMediaBuffer::Unlock stipulates // nothing about the state of the instance if this fails so might as // well just let it be released. //if (FAILED(hr)) break; releaseMBuffer: safeRelease(&pMBuffer); releaseSample: safeRelease(&pSample); if (error) break; } SINT framesRead = numberOfFrames - framesNeeded; m_iCurrentPosition += framesRead; m_nextFrame += framesRead; if (m_leftoverBufferLength > 0) { if (framesNeeded != 0) { qWarning() << __FILE__ << __LINE__ << "WARNING: Expected frames needed to be 0. Abandoning this file."; m_dead = true; } m_leftoverBufferPosition = m_nextFrame; } if (sDebug) { qDebug() << "read()" << numberOfFrames << "returning" << framesRead; } return framesRead; }
void ossimRpfToc::createTocAndCopyFrames( const ossimFilename& dotRpfFile, const ossimFilename& outputDir ) { static const char MODULE[] = "ossimRpfToc::createTocAndCopyFrames"; if ( traceDebug() ) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered..." << "\ndot rpf file: " << dotRpfFile << "\noutput directory: " << outputDir << "\n"; } if ( outputDir.expand().exists() == false ) { if ( !outputDir.createDirectory(true, 0775) ) { std::string e = MODULE; e += " ERROR:\nCould not create directory: "; e+= outputDir.c_str(); throw ossimException(e); } } // Open the dot rpf file. std::ifstream* dotRpfStr = new std::ifstream; dotRpfStr->open(dotRpfFile, ios_base::in); if ( !dotRpfStr->good() ) { delete dotRpfStr; dotRpfStr = 0; std::string e = MODULE; e += " ERROR:\nCould not open: "; e += dotRpfFile.c_str(); throw ossimException(e); } ossimFilename sourceADotTocFile = getSourceTocFile(*dotRpfStr); if ( sourceADotTocFile.empty() ) { delete dotRpfStr; dotRpfStr = 0; std::string e = MODULE; e += " ERROR:\nCould not deduce source a.toc file!"; throw ossimException(e); } // Open the source a.toc file. Note the true flag is to keep the file header. ossimRefPtr<ossimRpfToc> sourceADotToc = new ossimRpfToc; if ( sourceADotToc->parseFile(sourceADotTocFile, true) != ossimErrorCodes::OSSIM_OK ) { delete dotRpfStr; dotRpfStr = 0; std::string e = MODULE; e += " ERROR:\nCould not open: "; e += sourceADotTocFile.c_str(); throw ossimException(e); } ossimRefPtr<const ossimNitfFileHeader> sourceNitfFileHdr = sourceADotToc->getNitfFileHeader(); if ( !sourceNitfFileHdr.valid() ) { delete dotRpfStr; dotRpfStr = 0; std::string e = MODULE; e += " ERROR:\nCould not get nitf file header from: "; e += sourceADotTocFile.c_str(); throw ossimException(e); } ossimRefPtr<const ossimRpfHeader> sourceRpfHdr = sourceADotToc->getRpfHeader(); if ( !sourceRpfHdr.valid() ) { delete dotRpfStr; dotRpfStr = 0; std::string e = MODULE; e += " ERROR:\nCould not get rpf header from: "; e += sourceADotTocFile.c_str(); throw ossimException(e); } // Get the boundary rect sub header from the source a.toc. ossimRefPtr<ossimRpfBoundaryRectSectionSubheader> boundaryRectSectionSubheader = sourceRpfHdr->getNewBoundaryRectSectSubheader(sourceADotTocFile); if ( !boundaryRectSectionSubheader.valid() ) { delete dotRpfStr; dotRpfStr = 0; std::string e = MODULE; e += " ERROR:\nCould not pull boundary rect sub header from source file: "; e += sourceADotTocFile.c_str(); throw ossimException(e); } // Get the boundary rect table from the source a.toc. ossimRefPtr<ossimRpfBoundaryRectTable> boundaryRectTable = sourceRpfHdr->getNewBoundaryRectTable(sourceADotTocFile); if ( !boundaryRectTable.valid() ) { delete dotRpfStr; dotRpfStr = 0; std::string e = MODULE; e += " ERROR:\nCould not pull boundary rect table from source file: "; e += sourceADotTocFile.c_str(); throw ossimException(e); } // Get the frame file subheader from the source a.toc. ossimRefPtr<ossimRpfFrameFileIndexSectionSubheader> frameFileSubHeader = sourceRpfHdr->getNewFrameFileIndexSectionSubheader(sourceADotTocFile); if ( !frameFileSubHeader.valid() ) { delete dotRpfStr; dotRpfStr = 0; std::string e = MODULE; e += " ERROR:\nCould not pull frame file sub header from source file: "; e += sourceADotTocFile.c_str(); throw ossimException(e); } // Get the frame file subsection from the source a.toc. ossimRefPtr<ossimRpfFrameFileIndexSubsection> frameFileSubSection = sourceRpfHdr->getNewFileIndexSubsection(sourceADotTocFile); if ( !frameFileSubSection.valid() ) { delete dotRpfStr; dotRpfStr = 0; std::string e = MODULE; e += " ERROR:\nCould not pull frame file sub section from source file: "; e += sourceADotTocFile.c_str(); throw ossimException(e); } // Open the output file to write to. const ossimFilename A_DOT_TOC_FILE = "a.toc"; ossimFilename dotTocFile = outputDir.dirCat(A_DOT_TOC_FILE); std::ofstream* dotTocStr = new std::ofstream; dotTocStr->open( dotTocFile.c_str(), ios::out|ios::binary ); if ( !dotTocStr->good() ) { delete dotRpfStr; dotRpfStr = 0; delete dotTocStr; dotTocStr =0; std::string e = MODULE; e += " ERROR:\nCould not open: "; e += dotTocFile.c_str(); throw ossimException(e); } // Variables used throughout: ossimRefPtr<ossimProperty> prop = new ossimStringProperty(); ossimString field; ossimString s; std::streampos fileHeaderLength = 0; std::streampos fileLength = 0; ossimRefPtr<ossimNitfFileHeaderV2_0> fileHdr = new ossimNitfFileHeaderV2_0(); // Set the CLEVEL: s = "01"; fileHdr->setComplexityLevel(s); // Set the OSTAID: prop = sourceNitfFileHdr->getProperty(ossimNitfFileHeaderV2_X::OSTAID_KW); fileHdr->setProperty(prop); // Set the FDT (date): fileHdr->setDate(); // Set the FTITLE: s = "a.toc"; fileHdr->setTitle(s); // Set the FSCLAS: prop = sourceNitfFileHdr->getProperty(ossimNitfFileHeaderV2_X::FSCLAS_KW); fileHdr->setProperty(prop); // Set the FSCODE: prop = sourceNitfFileHdr->getProperty(ossimNitfFileHeaderV2_X::FSCODE_KW); fileHdr->setProperty(prop); // Set the FSCTLH: prop = sourceNitfFileHdr->getProperty(ossimNitfFileHeaderV2_X::FSCTLH_KW); fileHdr->setProperty(prop); // Set the ONAME: prop = sourceNitfFileHdr->getProperty(ossimNitfFileHeaderV2_X::ONAME_KW); fileHdr->setProperty(prop); // Set the OPHONE: prop = sourceNitfFileHdr->getProperty(ossimNitfFileHeaderV2_X::OPHONE_KW); fileHdr->setProperty(prop); // Add the rpf header. ossimRpfHeader* rpfHdr = new ossimRpfHeader( *(sourceRpfHdr.get()) ); ossimRefPtr<ossimNitfRegisteredTag> rpfHdrRp = rpfHdr; ossimNitfTagInformation rpfHdrInfo(rpfHdrRp); fileHdr->addTag(rpfHdrInfo); //--- // Write it out... // The first write will be with an rpfheader with no location sections just // to see where the end of the file header is. //--- fileHdr->writeStream(*dotTocStr); //--- // End of file header. Get the header length. This will also be the // start of the location section. //--- std::streampos pos = dotTocStr->tellp(); std::streamoff locationSectionOffset = pos; // Set the header length: fileHdr->setHeaderLength( static_cast<ossim_uint64>(locationSectionOffset) ); // Set the location of the location section. rpfHdr->setLocationSectionPos(locationSectionOffset); // Set the file name. rpfHdr->setFilename(A_DOT_TOC_FILE); // Add the component location records to the header. ossimRpfLocationSection* locSec = rpfHdr->getLocationSection(); // Clear the records copied from the source a.toc. locSec->clearFields(); //--- // Set the length of the locSec to 74. The record itself is 14 bytes plus // an additional 60 bytes for six location records ten bytes each. //--- const ossim_uint16 LOCATION_SECTION_SIZE = 74; locSec->setLocationSectionLength(LOCATION_SECTION_SIZE); // Set the offset which 14 bytes to get to the first record. locSec->setLocationTableOffset(14); // Six records: locSec->setNumberOfComponentLocationRecords(6); // Each record 10 bytes: locSec->setLocationRecordLength(10); // Don't know the aggregate length yet. ossimRpfComponentLocationRecord locRec; // Note: See ossimRpfConstants for enum ossimRpfComponentId const ossim_uint32 RPFHDR_SIZE = 48; const ossim_uint32 LOCATION_SECTION_OFFSET = static_cast<ossim_uint32>(locationSectionOffset); const ossim_uint32 BOUNDARY_SUBHEADER_SIZE = 8; const ossim_uint32 BOUNDARY_RECORD_SIZE = 132; const ossim_uint32 FILE_SUBHEADER_SIZE = 13; // const ossim_uint32 = ; // Record 1 RPFHDR location: ossim_uint32 rpfHdrOffset = 0; if ( fileHdr->getTag(rpfHdrInfo, "RPFHDR") ) { rpfHdrOffset = rpfHdrInfo.getTagDataOffset(); } locRec.m_componentId = OSSIM_RPF_HEADER_COMPONENT; // 128 locRec.m_componentLength = RPFHDR_SIZE; locRec.m_componentLocation = static_cast<ossim_uint32>(rpfHdrInfo.getTagDataOffset()); locSec->addComponentRecord(locRec); if ( traceDebug() ) { ossimNotify(ossimNotifyLevel_DEBUG)<< "rpf hdr offset: " << rpfHdrOffset << "\n"; locRec.print( ossimNotify(ossimNotifyLevel_DEBUG) ); } // Record 2 location section: locRec.m_componentId = OSSIM_RPF_LOCATION_COMPONENT; // 129 locRec.m_componentLength = LOCATION_SECTION_SIZE; locRec.m_componentLocation = LOCATION_SECTION_OFFSET; locSec->addComponentRecord(locRec); if ( traceDebug() ) { locRec.print( ossimNotify(ossimNotifyLevel_DEBUG) ); } // Record 3 boundary rect sub header section: locRec.m_componentId = OSSIM_RPF_BOUNDARY_RECT_SECTION_SUBHEADER; // 148 locRec.m_componentLength = BOUNDARY_SUBHEADER_SIZE; locRec.m_componentLocation = locRec.m_componentLocation + LOCATION_SECTION_SIZE; locSec->addComponentRecord(locRec); if ( traceDebug() ) { locRec.print( ossimNotify(ossimNotifyLevel_DEBUG) ); } // Capture the location. std::streamoff boundaryRectPosition = locRec.m_componentLocation; // Record 4 boundary rect table: locRec.m_componentId = OSSIM_RPF_BOUNDARY_RECT_TABLE; // 149 locRec.m_componentLength = BOUNDARY_RECORD_SIZE; locRec.m_componentLocation = locRec.m_componentLocation + BOUNDARY_SUBHEADER_SIZE; locSec->addComponentRecord(locRec); if ( traceDebug() ) { locRec.print( ossimNotify(ossimNotifyLevel_DEBUG) ); } // Record 5 file index sub header: locRec.m_componentId = OSSIM_RPF_FRAME_FILE_INDEX_SECTION_SUBHEADER; // 150 locRec.m_componentLength = FILE_SUBHEADER_SIZE; locRec.m_componentLocation = locRec.m_componentLocation + BOUNDARY_RECORD_SIZE; locSec->addComponentRecord(locRec); if ( traceDebug() ) { locRec.print( ossimNotify(ossimNotifyLevel_DEBUG) ); } // Record 6 file index sub header: locRec.m_componentId = OSSIM_RPF_FRAME_FILE_INDEX_SUBSECTION; // 151 locRec.m_componentLength = 0; // need to calculate. locRec.m_componentLocation = locRec.m_componentLocation + FILE_SUBHEADER_SIZE; locSec->addComponentRecord(locRec); if ( traceDebug() ) { locRec.print( ossimNotify(ossimNotifyLevel_DEBUG) ); } // Seek back and re-write... dotTocStr->seekp(0, ios::beg); fileHdr->writeStream(*dotTocStr); dotTocStr->seekp(boundaryRectPosition, ios::beg); // Only writing one entry: boundaryRectSectionSubheader->setNumberOfEntries(1); if ( traceDebug() ) { ossimNotify(ossimNotifyLevel_DEBUG) << "writing boundaryRectSectionSubheader:\n" << *(boundaryRectSectionSubheader.get()) << "\n"; } //--- // Write the boundary rectangle section. This includes the subheader and subsection. // These coorespond to location records 3 and 4 above. //--- boundaryRectSectionSubheader->writeStream(*dotTocStr); if ( traceDebug() ) { ossimNotify(ossimNotifyLevel_DEBUG) << "Original boundaryRectTable:\n" << *(boundaryRectTable.get()) << "\n"; } ossim_uint32 entry; if ( getCorespondingEntry( frameFileSubSection.get(), *dotRpfStr, entry ) ) { ossimRpfBoundaryRectRecord boundaryRectRecord; if ( boundaryRectTable->getEntry( entry, boundaryRectRecord) ) { if ( traceDebug() ) { ossimNotify(ossimNotifyLevel_DEBUG) << "writing boundaryRectTable:\n" << boundaryRectRecord << "\n"; } boundaryRectRecord.writeStream(*dotTocStr); } else { std::string e = MODULE; e += " ERROR:\nCould not get bounding rect record for entry: "; e += ossimString::toString(entry).c_str(); throw ossimException(e); } } else { std::string e = MODULE; e += " ERROR:\nCould not deduce entry from frame list!"; throw ossimException(e); } frameFileSubHeader->setNumberOfIndexRecords( getNumberOfFrames(*dotRpfStr) ); frameFileSubHeader->setNumberOfPathnameRecords(1); const ossim_uint16 FRAME_FILE_INDEX_RECORD_LENGTH = 33; frameFileSubHeader->setIndexRecordLength( FRAME_FILE_INDEX_RECORD_LENGTH ); if ( traceDebug() ) { ossimNotify(ossimNotifyLevel_DEBUG) << "writing frameFileSubHeader:\n" << *(frameFileSubHeader.get()) << "\n"; } frameFileSubHeader->writeStream( *dotTocStr ); if ( traceDebug() ) { ossimNotify(ossimNotifyLevel_DEBUG) << "writing frameFileSubSection:\n"; } std::streamoff frameFileIndexSectionStartPos = dotTocStr->tellp(); writeFrameFileIndexSection(frameFileSubSection.get(), *dotRpfStr, *dotTocStr); std::streamoff endOfFilePos = dotTocStr->tellp(); // Update the location section length for the frame file index section. locSec->getLocationRecordList()[5].m_componentLength = static_cast<ossim_uint32>(endOfFilePos - frameFileIndexSectionStartPos); // Update the length of all location sections. locSec->setComponentAggregateLength( static_cast<ossim_uint32>(endOfFilePos) - rpfHdr->getLocationSectionLocation() ); fileHdr->setFileLength(static_cast<ossim_uint64>(endOfFilePos)); dotTocStr->seekp(0, ios::beg); fileHdr->writeStream(*dotTocStr); ossimNotify(ossimNotifyLevel_DEBUG) << "Wrote file: " << dotTocFile << "\n"; // Copy the frames to the output directory. copyFrames(*dotRpfStr, outputDir); // Cleanup: delete dotRpfStr; dotRpfStr = 0; delete dotTocStr; dotTocStr =0; }
int AudioDecoderMediaFoundation::read(int size, const SAMPLE *destination) { assert(size < sizeof(m_destBufferShort)); if (sDebug) { std::cout << "read() " << size << std::endl; } //TODO: Change this up if we want to support just short samples again -- Albert SHORT_SAMPLE *destBuffer = m_destBufferShort; size_t framesRequested(size / m_iChannels); size_t framesNeeded(framesRequested); // first, copy frames from leftover buffer IF the leftover buffer is at // the correct frame if (m_leftoverBufferLength > 0 && m_leftoverBufferPosition == m_nextFrame) { copyFrames(destBuffer, &framesNeeded, m_leftoverBuffer, m_leftoverBufferLength); if (m_leftoverBufferLength > 0) { if (framesNeeded != 0) { std::cerr << __FILE__ << __LINE__ << "WARNING: Expected frames needed to be 0. Abandoning this file."; m_dead = true; } m_leftoverBufferPosition += framesRequested; } } else { // leftoverBuffer already empty or in the wrong position, clear it m_leftoverBufferLength = 0; } while (!m_dead && framesNeeded > 0) { HRESULT hr(S_OK); DWORD dwFlags(0); __int64 timestamp(0); IMFSample *pSample(NULL); bool error(false); // set to true to break after releasing hr = m_pReader->ReadSample( MF_SOURCE_READER_FIRST_AUDIO_STREAM, // [in] DWORD dwStreamIndex, 0, // [in] DWORD dwControlFlags, NULL, // [out] DWORD *pdwActualStreamIndex, &dwFlags, // [out] DWORD *pdwStreamFlags, ×tamp, // [out] LONGLONG *pllTimestamp, &pSample); // [out] IMFSample **ppSample if (FAILED(hr)) { if (sDebug) { std::cout << "ReadSample failed." << std::endl; } break; } if (sDebug) { std::cout << "ReadSample timestamp: " << timestamp << "frame: " << frameFromMF(timestamp) << "dwflags: " << dwFlags << std::endl; } if (dwFlags & MF_SOURCE_READERF_ERROR) { // our source reader is now dead, according to the docs std::cerr << "SSMF: ReadSample set ERROR, SourceReader is now dead"; m_dead = true; break; } else if (dwFlags & MF_SOURCE_READERF_ENDOFSTREAM) { std::cout << "SSMF: End of input file." << std::endl; break; } else if (dwFlags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED) { std::cerr << "SSMF: Type change"; break; } else if (pSample == NULL) { // generally this will happen when dwFlags contains ENDOFSTREAM, // so it'll be caught before now -bkgood std::cerr << "SSMF: No sample"; continue; } // we now own a ref to the instance at pSample IMFMediaBuffer *pMBuffer(NULL); // I know this does at least a memcopy and maybe a malloc, if we have // xrun issues with this we might want to look into using // IMFSample::GetBufferByIndex (although MS doesn't recommend this) if (FAILED(hr = pSample->ConvertToContiguousBuffer(&pMBuffer))) { error = true; goto releaseSample; } short *buffer(NULL); size_t bufferLength(0); hr = pMBuffer->Lock(reinterpret_cast<unsigned __int8**>(&buffer), NULL, reinterpret_cast<DWORD*>(&bufferLength)); if (FAILED(hr)) { error = true; goto releaseMBuffer; } bufferLength /= (m_iBitsPerSample / 8 * m_iChannels); // now in frames if (m_seeking) { __int64 bufferPosition(frameFromMF(timestamp)); if (sDebug) { std::cout << "While seeking to " << m_nextFrame << "WMF put us at " << bufferPosition << std::endl; } if (m_nextFrame < bufferPosition) { // Uh oh. We are farther forward than our seek target. Emit // silence? We can't seek backwards here. SHORT_SAMPLE* pBufferCurpos = destBuffer + (size - framesNeeded * m_iChannels); __int64 offshootFrames = bufferPosition - m_nextFrame; // If we can correct this immediately, write zeros and adjust // m_nextFrame to pretend it never happened. if (offshootFrames <= framesNeeded) { std::cerr << __FILE__ << __LINE__ << "Working around inaccurate seeking. Writing silence for" << offshootFrames << "frames"; // Set offshootFrames * m_iChannels samples to zero. memset(pBufferCurpos, 0, sizeof(*pBufferCurpos) * offshootFrames * m_iChannels); // Now m_nextFrame == bufferPosition m_nextFrame += offshootFrames; framesNeeded -= offshootFrames; } else { // It's more complicated. The buffer we have just decoded is // more than framesNeeded frames away from us. It's too hard // for us to handle this correctly currently, so let's just // try to get on with our lives. m_seeking = false; m_nextFrame = bufferPosition; std::cerr << __FILE__ << __LINE__ << "Seek offshoot is too drastic. Cutting losses and pretending the current decoded audio buffer is the right seek point."; } } if (m_nextFrame >= bufferPosition && m_nextFrame < bufferPosition + bufferLength) { // m_nextFrame is in this buffer. buffer += (m_nextFrame - bufferPosition) * m_iChannels; bufferLength -= m_nextFrame - bufferPosition; m_seeking = false; } else { // we need to keep going forward goto releaseRawBuffer; } } // If the bufferLength is larger than the leftover buffer, re-allocate // it with 2x the space. if (bufferLength * m_iChannels > m_leftoverBufferSize) { int newSize = m_leftoverBufferSize; while (newSize < bufferLength * m_iChannels) { newSize *= 2; } SHORT_SAMPLE* newBuffer = new SHORT_SAMPLE[newSize]; memcpy(newBuffer, m_leftoverBuffer, sizeof(m_leftoverBuffer[0]) * m_leftoverBufferSize); delete [] m_leftoverBuffer; m_leftoverBuffer = newBuffer; m_leftoverBufferSize = newSize; } copyFrames(destBuffer + (size - framesNeeded * m_iChannels), &framesNeeded, buffer, bufferLength); releaseRawBuffer: hr = pMBuffer->Unlock(); // I'm ignoring this, MSDN for IMFMediaBuffer::Unlock stipulates // nothing about the state of the instance if this fails so might as // well just let it be released. //if (FAILED(hr)) break; releaseMBuffer: safeRelease(&pMBuffer); releaseSample: safeRelease(&pSample); if (error) break; } m_nextFrame += framesRequested - framesNeeded; if (m_leftoverBufferLength > 0) { if (framesNeeded != 0) { std::cerr << __FILE__ << __LINE__ << "WARNING: Expected frames needed to be 0. Abandoning this file." << std::endl; m_dead = true; } m_leftoverBufferPosition = m_nextFrame; } long samples_read = size - framesNeeded * m_iChannels; m_iCurrentPosition += samples_read; if (sDebug) { std::cout << "read() " << size << " returning " << samples_read << std::endl; } const int sampleMax = 1 << (m_iBitsPerSample-1); //Convert to float samples if (m_iChannels == 2) { SAMPLE *destBufferFloat(const_cast<SAMPLE*>(destination)); for (unsigned long i = 0; i < samples_read; i++) { destBufferFloat[i] = destBuffer[i] / (float)sampleMax; } } else //Assuming mono, duplicate into stereo frames... { SAMPLE *destBufferFloat(const_cast<SAMPLE*>(destination)); for (unsigned long i = 0; i < samples_read; i++) { destBufferFloat[i] = destBuffer[i] / (float)sampleMax; } } return samples_read; }