質問

PCM WAVファイルを標準のAndroid SDKを使用して別のエンコードに再エンコードする方法はありますか?

マイクからこれらの形式に直接記録できることがわかりますが、私が書いているアプリは最初にPCMで記録する必要があります。ライセンスの抑制により、FFMPEGは利用可能なオプションではありません。


現在、Jelly Beanの次のコードがありますが、出力はメディアプレーヤーが読み取れません。

AOSPのステージ恐怖コードは、MPEG4コンテナを示唆しているようです

profile.nSampleRate = sampleRate;
profile.nBitRate = bitRate;
profile.nAudioBandWidth = 0;
profile.nFrameLength = 0;
profile.nAACtools = OMX_AUDIO_AACToolAll;
profile.nAACERtools = OMX_AUDIO_AACERNone;
profile.eAACProfile = (OMX_AUDIO_AACPROFILETYPE) aacProfile;
profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF;

ただし、Androidコードからの出力は読み取りできません。

入力WAVファイルは32kHz、必要に応じて16ビット署名Monoです。

public void doConvert( View v)
{
    new AsyncTask<Void, Void, Void>()
    {

        @Override
        protected Void doInBackground(Void... params) 
        {
            try
            {
                int codecCount = MediaCodecList.getCodecCount();

                for ( int i=0; i < codecCount; i++)
                {
                    MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
                    Logger.getLogger(MainActivity.class.getSimpleName()).log(Level.INFO, info.getName());
                    for ( String type : info.getSupportedTypes() )
                    {
                        Logger.getLogger(MainActivity.class.getSimpleName()).log(Level.INFO, type);
                    }

                }

                File inputFile = new File( Environment.getExternalStorageDirectory().getAbsolutePath()+"/Media/Report-test5.wav");
                FileInputStream fis = new FileInputStream(inputFile);
                fis.skip(44);//remove wav header

                File outputFile = new File( Environment.getExternalStorageDirectory().getAbsolutePath()+"/Media/out.mp4");
                if ( outputFile.exists()) outputFile.delete();

                FileOutputStream fos = new FileOutputStream(outputFile);

                MediaCodec codec = MediaCodec.createEncoderByType("audio/mp4a-latm");

                MediaFormat outputFormat = MediaFormat.createAudioFormat("audio/mp4a-latm", 32000, 1);
                outputFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
                //outputFormat.setInteger(MediaFormat.KEY_CHANNEL_MASK, AudioFormat.CHANNEL_OUT_MONO);
                outputFormat.setInteger(MediaFormat.KEY_BIT_RATE, 48000 );
                //outputFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 64000);
                double durationInMs = (inputFile.length()/64.0)*1000.0;

                outputFormat.setLong(MediaFormat.KEY_DURATION, (long)durationInMs );
                //Logger.getLogger(MainActivity.class.getSimpleName()).log(Level.INFO, codec.getOutputFormat().toString());

                codec.configure(outputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE );
                codec.start();

                ByteBuffer[] inputBuffers = codec.getInputBuffers();
                ByteBuffer[] outputBuffer = codec.getOutputBuffers();

                boolean hasMoreData = true;
                MediaCodec.BufferInfo outBuffInfo = new BufferInfo();
                byte readBuffer[] = new byte[64000];
                byte writeBuffer[] = new byte[64000];

                do
                {
                    int nextBuffer = codec.dequeueInputBuffer(1000);
                    logger.log(Level.INFO,"nextInputBuffer = "+nextBuffer);

                    if ( nextBuffer >= 0 )
                    {



                        ByteBuffer inBuf = inputBuffers[nextBuffer];
                        inBuf.clear();
                        int bytesRead = fis.read( readBuffer,0, inBuf.capacity() );
                        logger.log(Level.INFO,"Read = "+bytesRead);

                        if ( bytesRead < inBuf.capacity() )
                        {
                            hasMoreData = false;
                        }

                        inBuf.put(readBuffer, 0, bytesRead );

                        codec.queueInputBuffer(nextBuffer, 0, bytesRead, 0, hasMoreData?0:MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                    }


                    int outputBufferIndex = codec.dequeueOutputBuffer( outBuffInfo, 1000 );
                    logger.log(Level.INFO,"nextOutputBuffer = "+outputBufferIndex);
                    logger.log(Level.INFO,"outBuffInfo offset = "+outBuffInfo.offset);
                    logger.log(Level.INFO,"outBuffInfo size = "+outBuffInfo.size);
                    logger.log(Level.INFO,"outBuffInfo flags = "+outBuffInfo.flags);


                    //while ( outputBufferIndex > -1 )
                    //{ 

                        outputBuffer[outputBufferIndex].position(outBuffInfo.offset);
                        outputBuffer[outputBufferIndex].get(writeBuffer,0,outBuffInfo.size);

                        fos.write(writeBuffer,0, outBuffInfo.size);
                        logger.log(Level.INFO,"Writing = "+outBuffInfo.size+" bytes");


                        outputBuffer[outputBufferIndex].clear();

                        codec.releaseOutputBuffer(outputBufferIndex, false);

                        if ( outBuffInfo.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM )
                        {
                            codec.flush();
                            codec.stop();
                            codec.release();
                            break;
                        }

                        //outputBufferIndex = codec.dequeueOutputBuffer( outBuffInfo, 1000 );
                        //logger.log(Level.INFO,"nextOutputBuffer = "+outputBufferIndex);
                    //}

                } while ( outBuffInfo.flags != MediaCodec.BUFFER_FLAG_END_OF_STREAM);

                fis.close();
                fos.flush();
                fos.close();



            }
            catch ( Exception e)
            {
                Logger.getLogger(MainActivity.class.getSimpleName()).log(Level.INFO, "Codec Error",e);
            }

            logger.log(Level.INFO,"Done");

            return null;
        }

    }.execute();
}
役に立ちましたか?

解決

基本的に、生成されたすべてのエンコードされたバッファーをファイルに記述していますが、あらゆる種類が欠落しています メタデータ トラック/サンプル/ファイルなどに関連しています。

私が考えることができる解決策は次のとおりです:あなたが良いものを見つけることができるかのどちらか マクサー バッファーを適切なファイル形式に書き込むライブラリ、または将来のAndroidがそのようなAPIを提供するかどうかを待って確認する必要があります。

他のヒント

私自身の質問に答えるために、

Android 4.3以降には、オーディオおよびビデオストリーム用にMPEG4コンテナを作成するMediAMuxerクラスがあります。

public boolean encode( File outputFile )

{

if ( Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2 )
{
    try
    {
        mInputWav.close();
    }
    catch (IOException e)
    {
        logger.log(Level.WARNING,"Unable to close Input Wav File ",e);
    }

    throw new UnsupportedOperationException("Only Available on Android 4.3 and Above");
}

try
{

    int sampleRate = mInputWav.getSampleRate();
    int percentComplete = 0;
    int fileSize = mInputWav.getDataLength();
    int totalBytesRead = 0;

    if ( outputFile.exists()) outputFile.delete();

    MediaMuxer mux = new MediaMuxer( outputFile.getAbsolutePath(), OutputFormat.MUXER_OUTPUT_MPEG_4);

    MediaCodec codec = MediaCodec.createEncoderByType("audio/mp4a-latm");

    MediaFormat outputFormat = MediaFormat.createAudioFormat("audio/mp4a-latm",sampleRate,1);
    outputFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
    outputFormat.setInteger(MediaFormat.KEY_BIT_RATE, 128000 );

    codec.configure(outputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE );
    codec.start();

    ByteBuffer[] inputBuffers = codec.getInputBuffers();
    ByteBuffer[] outputBuffer = codec.getOutputBuffers();

    boolean hasMoreData = true;
    MediaCodec.BufferInfo outBuffInfo = new BufferInfo();
    byte readBuffer[] = new byte[64000];
    double presentationTimeUs=0;
    int audioTrackIdx=0;

    do
    {

        int nextBuffer = 0;
        while ( nextBuffer != -1  && hasMoreData )
        {
            nextBuffer = codec.dequeueInputBuffer(1000);

            if ( nextBuffer >= 0 )
            {
                ByteBuffer inBuf = inputBuffers[nextBuffer];
                inBuf.clear();
                int bytesRead = mInputWav.read( readBuffer,0, inBuf.limit() );

                if ( bytesRead == -1 )
                {
                    hasMoreData = false;                                
                    codec.queueInputBuffer(nextBuffer, 0, 0, (long)presentationTimeUs, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                }
                else
                {

                    totalBytesRead += bytesRead;                            
                    inBuf.put(readBuffer, 0, bytesRead );

                    codec.queueInputBuffer(nextBuffer, 0, bytesRead, (long)presentationTimeUs, 0);

                    presentationTimeUs  = 1000000l * (totalBytesRead) / 2 / sampleRate;
                }
            }

        }


        //Drain audio
        int outputBufferIndex = 0;
        while (outputBufferIndex != MediaCodec.INFO_TRY_AGAIN_LATER )
        {
            outputBufferIndex = codec.dequeueOutputBuffer( outBuffInfo, 10000 );
            if ( outputBufferIndex >= 0 )
            {

                ByteBuffer encodedData = outputBuffer[outputBufferIndex];
                encodedData.position(outBuffInfo.offset);
                encodedData.limit(outBuffInfo.offset + outBuffInfo.size );

                if ((outBuffInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG)!= 0 && outBuffInfo.size != 0) 
                {
                    logger.log(Level.FINE, "video encoder: codec config buffer");
                    // Simply ignore codec config buffers.
                    codec.releaseOutputBuffer(outputBufferIndex, false);

                }
                else
                {
                    mux.writeSampleData(audioTrackIdx, outputBuffer[outputBufferIndex], outBuffInfo);
                    codec.releaseOutputBuffer(outputBufferIndex, false);
                }

            }
            else if ( outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED )
            {
                logger.info("Output Format Changed :"+codec.getOutputFormat().toString());
                outputFormat = codec.getOutputFormat();
                audioTrackIdx = mux.addTrack(outputFormat);
                mux.start();

            }
            else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED )
            {
                logger.info("Output Buffers Changed: shouldn't happen on an encode ");
            }
            else if ( outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER)
            {
                logger.info("Encoder Timed Out");
            }
            else
            {
                logger.info("Unkown return code from dequeueOutputBuffer "+outputBufferIndex);
            }
        }

        percentComplete = (int)Math.round(((float)totalBytesRead / (float)fileSize)*100.0);
        logger.info("Percent Complete "+percentComplete+"%");

        if ( mListener != null )
        {
            mListener.onProgressUpdate(percentComplete);
        }

    } while ( outBuffInfo.flags != MediaCodec.BUFFER_FLAG_END_OF_STREAM && !mCanceled);

    mInputWav.close();
    mux.stop();
    mux.release();

    logger.info("Finished");



}
catch ( Exception e)
{
    logger.log(Level.INFO, "Codec Error",e);
}


logger.log(Level.INFO,"Done");

return true;

}

そのためのコンテナを選択してください。私もADTを好みますが、FLV/MP4も機能します。

ペイロードデータをコンテナに十分な大きさのArayにコピーして、ビットを追加してください。だから私のソリューションのためにインターネットを精査した後、私はいくつかのスニペットを所定の位置に動かしました

    profile =( configParams[0]>>3 )&0x1f;

    frequency_index = (this.configParams[0]&0x7) <<1 | (this.configParams[1]>>7) &0x1;

    channel_config = (this.configParams[1]>>3) &0xf;

    int finallength = encoded_length + 7;       
    ENCodedByteArray[0] = (byte) 0xff;
    ENCodedByteArray[1] = (byte) 0xf1;
    ENCodedByteArray[2] = (byte) ( ((profile - 1) << 6) + (frequency_index << 2) +(channel_config >> 2));
    ENCodedByteArray[3] = (byte) (((channel_config & 0x3) << 6) + (finallength >> 11));
    ENCodedByteArray[4] = (byte)( (finallength & 0x7ff) >> 3);
    ENCodedByteArray[5] = (byte) (((finallength & 7) << 5) + 0x1f) ;
    ENCodedByteArray[6] = (byte) 0xfc;

以下のようなものを使用して、上記を呼び出す

            byte chunkADTS[]=new byte[info.size + 7];
            fillInADTSHeader(chunkADTS,info.size);
            outputBuffers[bR].get(chunkADTS,7,info.size);
            buffer.pushData(chunkADTS);

ヘッダー情報が含まれていないため、ファイルを再生できません。このプレイ可能にする簡単な(ISH)方法は、ADTSヘッダーを生のAACフレームに追加することです。説明を見つけることができます ここ.

幸運を!

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top