AudioFile::SampleType AudioFileReaderInternal::getSampleType() const throw()
{    
    const AudioFile::Encoding encoding = getEncoding();
    const int bytesPerSample = getBytesPerSample();
    const bool isPCM = encoding & AudioFile::EncodingFlagPCM;
    const bool isFloat = encoding & AudioFile::EncodingFlagFloat;

    if (isPCM)
    {        
        switch (bytesPerSample)
        {
            case 2: return AudioFile::Short;
            case 3: return AudioFile::Int24;
            case 4: return AudioFile::Int;
            case 1: return AudioFile::Char;
            default: goto exit;
        }        
    }
    else if (isFloat)
    {
        switch (bytesPerSample)
        {
            case 4: return AudioFile::Float;
            case 8: return AudioFile::Double;
            default: goto exit;
        }                
    }
    
exit:
    return AudioFile::Invalid;
}
Exemple #2
0
void AudioDecoderThread::planarToInterleaved(char* pOutput, AVFrame* pInputFrame, int numChannels,
        int numSamples)
{
    AVG_ASSERT(numChannels <= 8);
    if (numSamples == 0) {
        // Fishy, some ogg files have no proper frame_size set. But outputBufferSamples
        // worked for sample ogg file.
        numSamples = m_AP.m_OutputBufferSamples;
    }
    int i, j;
    int bytesPerSample = getBytesPerSample(m_InputSampleFormat);
    char * pPlanes[8] = {};
    for (i=0; i<numChannels; i++) {
        pPlanes[i] = (char*)(pInputFrame->data[i]);
    }
    for (i=0; i<numSamples; i++) {
        for (j=0; j<numChannels; j++) {
            memcpy(pOutput, pPlanes[j], bytesPerSample);
            pOutput += bytesPerSample;
            pPlanes[j] += bytesPerSample;
        }
    }
}
Exemple #3
0
void AudioDecoderThread::decodePacket(AVPacket* pPacket)
{
    char* pDecodedData = 0;
    AVPacket* pTempPacket = new AVPacket;
    av_init_packet(pTempPacket);
    pTempPacket->data = pPacket->data;
    pTempPacket->size = pPacket->size;
    AVFrame* pDecodedFrame;
    pDecodedFrame = avcodec_alloc_frame();
    while (pTempPacket->size > 0) {
        int gotFrame = 0;
        int bytesDecoded;
        int bytesConsumed = avcodec_decode_audio4(m_pStream->codec, pDecodedFrame,
                &gotFrame, pTempPacket);
        if (gotFrame) {
            bytesDecoded = av_samples_get_buffer_size(0, m_pStream->codec->channels,
                    pDecodedFrame->nb_samples, m_pStream->codec->sample_fmt, 1);
            pDecodedData = (char*)(pDecodedFrame->data[0]);
        } else {
            bytesDecoded = 0;
        }
//        This is triggered for some strange/broken videos.
//        AVG_ASSERT(bytesConsumed != 0);
        if (bytesConsumed < 0) {
            // Error decoding -> throw away current packet.
            bytesDecoded = 0;
            pTempPacket->size = 0;
        } else {
            pTempPacket->data += bytesConsumed;
            pTempPacket->size -= bytesConsumed;
        }
        if (bytesDecoded > 0) {
            int framesDecoded = bytesDecoded/(m_pStream->codec->channels*
                    getBytesPerSample(m_InputSampleFormat));
            AudioBufferPtr pBuffer;
            bool bNeedsResample = (m_InputSampleRate != m_AP.m_SampleRate ||
                    m_InputSampleFormat != AV_SAMPLE_FMT_S16 ||
                    m_pStream->codec->channels != m_AP.m_Channels);
            bool bIsPlanar = false;
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 27, 0)            
            bIsPlanar = av_sample_fmt_is_planar((AVSampleFormat)m_InputSampleFormat);
            if (bIsPlanar) {
                char* pPackedData = (char*)av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE +
                        FF_INPUT_BUFFER_PADDING_SIZE);
                planarToInterleaved(pPackedData, pDecodedFrame, m_pStream->codec->channels,
                        framesDecoded);
                pBuffer = resampleAudio(pPackedData, framesDecoded,
                        av_get_packed_sample_fmt((AVSampleFormat)m_InputSampleFormat));
                av_free(pPackedData);
                bNeedsResample = false;
            }
#endif
            if (bNeedsResample) {
                pBuffer = resampleAudio(pDecodedData, framesDecoded,
                        m_InputSampleFormat);
            } else if (!bIsPlanar) {
                pBuffer = AudioBufferPtr(new AudioBuffer(framesDecoded, m_AP));
                memcpy(pBuffer->getData(), pDecodedData, bytesDecoded);
            }
            m_LastFrameTime += float(pBuffer->getNumFrames())/m_AP.m_SampleRate;
            pushAudioMsg(pBuffer, m_LastFrameTime);
        }
    }
#if LIBAVCODEC_VERSION_MAJOR > 53
    avcodec_free_frame(&pDecodedFrame);
    delete pDecodedFrame;
#endif
    delete pTempPacket;
}
TargetFileMediaFoundation::TargetFileMediaFoundation( const DataTargetRef &dataTarget, size_t sampleRate, size_t numChannels, SampleType sampleType, const std::string &extension )
	: TargetFile( dataTarget, sampleRate, numChannels, sampleType ), mStreamIndex( 0 )
{
	MediaFoundationInitializer::initMediaFoundation();

	::IMFSinkWriter *sinkWriter;
	HRESULT hr = ::MFCreateSinkWriterFromURL( dataTarget->getFilePath().wstring().c_str(), NULL, NULL, &sinkWriter );
	if( hr != S_OK ) {
		string errorString = string( "TargetFileMediaFoundation: Failed to create SinkWriter from URL: " ) +  dataTarget->getFilePath().string(); 
		if( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) )
			errorString += ", file not found.";
		throw AudioFileExc( errorString );
	}

	mSinkWriter = ci::msw::makeComUnique( sinkWriter );

	mSampleSize = getBytesPerSample( mSampleType );
	const UINT32 bitsPerSample = 8 * mSampleSize;
	const WORD blockAlignment = mNumChannels * mSampleSize;
	const DWORD averageBytesPerSecond = mSampleRate * blockAlignment;

	// Set the output media type.

	IMFMediaType *mediaType;
	hr = ::MFCreateMediaType( &mediaType );
	CI_ASSERT( hr == S_OK );
	auto mediaTypeManaged = ci::msw::makeComUnique( mediaType );

	hr = mediaType->SetGUID( MF_MT_MAJOR_TYPE, MFMediaType_Audio );
	CI_ASSERT( hr == S_OK );

	const ::GUID audioFormat = getMfAudioFormat( mSampleType );
	hr = mediaType->SetGUID( MF_MT_SUBTYPE, audioFormat );
	CI_ASSERT( hr == S_OK );

	hr = mediaType->SetUINT32( MF_MT_AUDIO_SAMPLES_PER_SECOND, (UINT32)mSampleRate );
	CI_ASSERT( hr == S_OK );

	hr = mediaType->SetUINT32( MF_MT_AUDIO_BITS_PER_SAMPLE, bitsPerSample );
	CI_ASSERT( hr == S_OK );

	hr = mediaType->SetUINT32( MF_MT_AUDIO_BLOCK_ALIGNMENT, blockAlignment );
	CI_ASSERT( hr == S_OK );

	hr = mediaType->SetUINT32( MF_MT_AUDIO_AVG_BYTES_PER_SECOND, averageBytesPerSecond );
	CI_ASSERT( hr == S_OK );

	hr = mediaType->SetUINT32( MF_MT_AUDIO_NUM_CHANNELS, (UINT32)mNumChannels );
	CI_ASSERT( hr == S_OK );

	hr = mediaType->SetUINT32( MF_MT_ALL_SAMPLES_INDEPENDENT, 1 );
	CI_ASSERT( hr == S_OK );

	hr = mediaType->SetUINT32( MF_MT_FIXED_SIZE_SAMPLES, 1 );
	CI_ASSERT( hr == S_OK );

	hr = mSinkWriter->AddStream( mediaType, &mStreamIndex );
	CI_ASSERT( hr == S_OK );

	// Tell the sink writer to start accepting data.
	hr = mSinkWriter->BeginWriting();
	CI_ASSERT( hr == S_OK );
}
// TODO: test setting MF_LOW_LATENCY attribute
void SourceFileMediaFoundation::initReader()
{
	CI_ASSERT( mDataSource );
	mFramesRemainingInReadBuffer = 0;

	::IMFAttributes *attributes;
	HRESULT hr = ::MFCreateAttributes( &attributes, 1 );
	CI_ASSERT( hr == S_OK );
	auto attributesPtr = ci::msw::makeComUnique( attributes );

	::IMFSourceReader *sourceReader;

	if( mDataSource->isFilePath() ) {
		hr = ::MFCreateSourceReaderFromURL( mDataSource->getFilePath().wstring().c_str(), attributesPtr.get(), &sourceReader );
		if( hr != S_OK ) {
			string errorString = string( "SourceFileMediaFoundation: Failed to create SourceReader from URL: " ) +  mDataSource->getFilePath().string(); 
			if( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) )
				errorString += ", file not found.";
			throw AudioFileExc( errorString );
		}
	}
	else {
		mComIStream = ci::msw::makeComUnique( new ci::msw::ComIStream( mDataSource->createStream() ) );
		::IMFByteStream *byteStream;
		hr = ::MFCreateMFByteStreamOnStream( mComIStream.get(), &byteStream );
		CI_ASSERT( hr == S_OK );
		mByteStream = ci::msw::makeComUnique( byteStream );

		hr = ::MFCreateSourceReaderFromByteStream( byteStream, attributesPtr.get(), &sourceReader );
		if( hr != S_OK )
			throw AudioFileExc( "SourceFileMediaFoundation: Failed to create SourceReader from resource." );
	}

	mSourceReader = ci::msw::makeComUnique( sourceReader );

	// get files native format
	::IMFMediaType *nativeType;
	::WAVEFORMATEX *nativeFormat;
	UINT32 formatSize;
	hr = mSourceReader->GetNativeMediaType( MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, &nativeType );
	CI_ASSERT( hr == S_OK );
	auto nativeTypeUnique = ci::msw::makeComUnique( nativeType );

	hr = ::MFCreateWaveFormatExFromMFMediaType( nativeType, &nativeFormat, &formatSize );
	CI_ASSERT( hr == S_OK );

	mNumChannels = nativeFormat->nChannels;
	mSampleRate = nativeFormat->nSamplesPerSec;

	GUID outputSubType = MFAudioFormat_PCM; // default to PCM 16-bit int, upgrade if we can.
	mSampleType = SampleType::INT_16;

	if( nativeFormat->wBitsPerSample == 32 ) {
		mSampleType = SampleType::FLOAT_32;
		outputSubType = MFAudioFormat_Float;
	}
	else if( nativeFormat->wBitsPerSample == 24 ) {
		mSampleType = SampleType::INT_24;
		if( mNumChannels > 1 )
			mBitConverterBuffer.setSize( getMaxFramesPerRead(), mNumChannels );
	}

	::CoTaskMemFree( nativeFormat );

	mBytesPerSample = getBytesPerSample( mSampleType );
	mReadBuffer.setSize( getMaxFramesPerRead(), mNumChannels );

	// set output type, which loads the proper decoder:
	::IMFMediaType *outputType;
	hr = ::MFCreateMediaType( &outputType );
	auto outputTypeRef = ci::msw::makeComUnique( outputType );
	hr = outputTypeRef->SetGUID( MF_MT_MAJOR_TYPE, MFMediaType_Audio );
	CI_ASSERT( hr == S_OK );
	hr = outputTypeRef->SetGUID( MF_MT_SUBTYPE, outputSubType );
	CI_ASSERT( hr == S_OK );

	hr = mSourceReader->SetCurrentMediaType( MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, outputTypeRef.get() );
	CI_ASSERT( hr == S_OK );

	// after the decoder is loaded, we have to now get the 'complete' output type before retrieving its format
	::IMFMediaType *completeOutputType;
	hr = mSourceReader->GetCurrentMediaType( MF_SOURCE_READER_FIRST_AUDIO_STREAM, &completeOutputType );
	CI_ASSERT( hr == S_OK );

	::WAVEFORMATEX *format;
	hr = ::MFCreateWaveFormatExFromMFMediaType( completeOutputType, &format, &formatSize );
	CI_ASSERT( hr == S_OK );
	::CoTaskMemFree( format );

	// get seconds:
	::PROPVARIANT durationProp;
	hr = mSourceReader->GetPresentationAttribute( MF_SOURCE_READER_MEDIASOURCE, MF_PD_DURATION, &durationProp );
	CI_ASSERT( hr == S_OK );
	LONGLONG duration = durationProp.uhVal.QuadPart;
	
	mSeconds = nanoSecondsToSeconds( duration );
	mNumFrames = mFileNumFrames = size_t( mSeconds * (double)mSampleRate );

	::PROPVARIANT seekProp;
	hr = mSourceReader->GetPresentationAttribute( MF_SOURCE_READER_MEDIASOURCE, MF_SOURCE_READER_MEDIASOURCE_CHARACTERISTICS, &seekProp );
	CI_ASSERT( hr == S_OK );
	ULONG flags = seekProp.ulVal;
	mCanSeek = ( ( flags & MFMEDIASOURCE_CAN_SEEK ) == MFMEDIASOURCE_CAN_SEEK );
}
IFrame* AudioFrameBuffer::getFrameSampleNb(const size_t sampleNb)
{
    const size_t expectedSize = sampleNb * getBytesPerSample();
    return getFrame(expectedSize);
}