Question

Existe-t-il un moyen de recoder un fichier wav PCM vers un autre encodage à l'aide du SDK Android standard?

Je peux voir qu'il est possible d'enregistrer directement depuis le micro dans ces formats, mais l'application que j'écris doit d'abord enregistrer en PCM. En raison de restrictions de licence, ffmpeg n'est pas une option disponible.


J'ai maintenant le code suivant pour Jelly bean mais la sortie n'est lisible par aucun lecteur multimédia.

Le code de trac d'Aosp semble suggérer un conteneur 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;

Mais la sortie du code Android n'est pas lisible.

Le fichier wav d'entrée est de 32 kHz, 16 bits signé mono comme requis.

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();
}
Était-ce utile?

La solution

Fondamentalement, vous écrivez tous les tampons encodés générés dans un fichier, mais il manque toutes sortes de méta-données liées aux pistes / échantillons / fichier, etc.

Je suppose que les solutions auxquelles je peux penser sont: soit vous pouvez trouver une bonne bibliothèque muxer pour écrire les tampons dans le bon format de fichier, soit vous devez attendre de voir si le futur Android le ferafournir ces API pour vous.

Autres conseils

Pour répondre à ma propre question,

Dans Android 4.3 et supérieur, il existe la classe MediaMuxer qui crée des conteneurs MPEG4 pour le flux audio et vidéo.

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;

}

Choisissez un conteneur pour cela.Je préfère aussi les publicités, mais flv / mp4 fonctionne aussi.

Copiez les données utiles dans un aray suffisamment grand pour votre conteneur, ajoutez simplement vos bits.Donc, après avoir parcouru Internet à la recherche de ma solution, j'ai mis un extrait de code en place

    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;

En utilisant quelque chose comme ci-dessous, qui appelle ce qui précède

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

Le fichier ne peut pas être lu car il ne contient aucune information d'en-tête.Un moyen simple (ish) de rendre cela jouable est d'ajouter des en-têtes ADTS aux images AAC brutes.Vous pouvez trouver une description ici .

Bonne chance!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top