溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

視頻編解碼技術初探

發布時間:2020-07-14 21:56:07 來源:網絡 閱讀:578 作者:屠夫章哥 欄目:移動開發
一、需要閱讀的文章
        https://blog.csdn.net/feifeiwendao/article/details/52527824

        ####MediaCodec類相關的文章:########
        https://www.cnblogs.com/renhui/p/7478527.html

        https://blog.csdn.net/junzia/article/details/54018671    (思路很清晰的一篇文章)

        https://www.cnblogs.com/roger-yu/p/5635494.html

        https://www.cnblogs.com/Sharley/p/5964490.html

        https://blog.csdn.net/leif_/article/details/50971616

        MediaCodec實際上就是一個編解碼的容器,將byte[]放進去,取出來就得到自己想要的編碼格式的byte[],然后
        寫入文件即可。

        #########AudioRecorder錄音:###################
        https://blog.csdn.net/qq_36982160/article/details/79383046

        https://www.cnblogs.com/whoislcj/p/5477216.html

        錄音的流程:
        1)初始化AudioRecorder
        2)調用AudioRecorder的startRecording方法
        3)調用AudioRecorder的read方法,讀出byte[]數據。你可以選擇將數據存儲到文件,最終得到的是.pcm文件。
             這種文件一般播放器不支持,需要用andorid的AudioTrack去播放。

        AudioTrack,播放PCM音頻文件。
        https://www.cnblogs.com/stnlcd/p/7151438.html
        注意:AudioTrack的構造參數,要與錄制時的參數保持一致 。如channelConfig如果不對應
        則人說話的聲音就超級奇怪了。

        以下是PCM錄音和播放的源碼: 
package com.xinyi.czsuperrecorder;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaRecorder;
import android.util.Log;

import com.xinyi.baselib.io.tf.TFileHelper;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * Created by XinYi on 2018/7/5.
 * (錄音API)
 */
public class AudioRecorder {
    private static final String TAG = "AudioRecorder";
    private int sampleRate=44100;   //采樣率,默認44.1k
    private int channelConfig= AudioFormat.CHANNEL_IN_STEREO;        //通道設置,默認立體聲
    private int audioFormat=AudioFormat.ENCODING_PCM_16BIT;     //設置采樣數據格式,默認16比特PCM
    private AudioRecord mRecorder;
    private int bufferSize;
    private File recordingFile = new File(TFileHelper.getInstance().getRoot() + "/test/a.pcm");

    public void prepare(){
        bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat)*2;
        //        buffer=new byte[bufferSize];
        mRecorder=new AudioRecord(MediaRecorder.AudioSource.MIC,sampleRate,channelConfig,
                audioFormat,bufferSize);
    }

    public void start() throws IOException {
        byte[] tempBuffer = new byte[bufferSize];
        if (mRecorder.getState() != AudioRecord.STATE_INITIALIZED) {
            stop();
            return;
        }

        //開始錄制
        mRecorder.startRecording();
        //循環讀取數據到buffer中,并保存buffer中的數據到文件中
        Log.e(TAG, "start: 錄音中。。。");

        TFileHelper.getInstance().deleteFile(recordingFile.getAbsolutePath());
        TFileHelper.getInstance().createFile(recordingFile.getAbsolutePath());
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(recordingFile);
            int length;
            while ((length = mRecorder.read(tempBuffer, 0, bufferSize)) != -1) {
                fileOutputStream.write(tempBuffer, 0, length);
                fileOutputStream.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileOutputStream != null) {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public void stop(){
        //中止循環并結束錄制
        mRecorder.stop();
        Log.e(TAG, "start: 錄音結束。。。");
    }

    //播放音頻(PCM)
    public void play()
    {
        DataInputStream dis=null;
        try {
            //從音頻文件中讀取聲音
            dis=new DataInputStream(new BufferedInputStream(new FileInputStream(recordingFile)));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        //最小緩存區
        int bufferSizeInBytes= AudioTrack.getMinBufferSize(sampleRate,AudioFormat.CHANNEL_OUT_MONO,AudioFormat.ENCODING_PCM_16BIT);
        //創建AudioTrack對象   依次傳入 :流類型、采樣率(與采集的要一致)、音頻通道(采集是IN 播放時OUT)、量化位數、最小緩沖區、模式
        /**
         * !!注意,音頻通道與錄制時的音頻通道要保持一致。
         */
        AudioTrack player=new AudioTrack(AudioManager.STREAM_MUSIC,sampleRate,AudioFormat.CHANNEL_OUT_STEREO,AudioFormat.ENCODING_PCM_16BIT, bufferSizeInBytes, AudioTrack.MODE_STREAM);

        byte[] data =new byte [bufferSizeInBytes];
        player.play();//開始播放
        while(true)
        {
            int i=0;
            try {
                while(dis.available()>0&&i<data.length)
                {
                    data[i]=dis.readByte();
                    i++;
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            player.write(data,0,data.length);

            if(i!=bufferSizeInBytes) //表示讀取完了
            {
                player.stop();//停止播放
                player.release();//釋放資源
                break;
            }
        }

    }

    public AudioRecord getmRecorder() {
        return mRecorder;
    }

    public int getBufferSize() {
        return bufferSize;
    }
}

二、緩沖區
https://blog.csdn.net/bzlj2912009596/article/details/75581675

三、音頻AAC編碼
關于AAC編碼的文章:
https://blog.csdn.net/jay100500/article/details/52955232/ (AAC的頭文件,介紹了一款工具可以查看aac文件的頭文件)

     音頻AAC編碼實現過程
     1)開啟音頻錄制
     2)通過MediaCodec,將音頻編碼,得到AAC裸流,加上AAC頭,然后再寫入文件。
     代碼如下:
         package com.xinyi.czsuperrecorder.code.audio;

import android.media.AudioRecord;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.util.Log;

import com.xinyi.baselib.io.tf.TFileHelper;
import com.xinyi.czsuperrecorder.Config;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

/**
 * Created by XinYi on 2018/7/5.
 * AAC編碼
 */
public class AACEncoder {
    private static final String TAG = "AACEncoder";

    private String mime = "audio/mp4a-latm";    //錄音編碼的mime
    private int rate=256000;                    //編碼的key bit rate
    private  MediaCodec mEnc;
    private AudioRecorder audioRecorder;
    private File recordingFile = new File(TFileHelper.getInstance().getRoot() + "/test/a.m4a");
    private boolean isStopped = true;
    private int bufferSize;

    public AACEncoder(AudioRecorder audioRecorder) {
        this.audioRecorder = audioRecorder;
        isStopped = true;
    }

    public void prepare(){
        audioRecorder.prepare();
        try {
            bufferSize = 0;
            //相對于上面的音頻錄制,我們需要一個編碼器的實例
            MediaFormat format=MediaFormat.createAudioFormat(mime, Config.sampleRate,Config.channelCount);
            format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
            format.setInteger(MediaFormat.KEY_BIT_RATE, rate);
            mEnc= MediaCodec.createEncoderByType(mime);
            mEnc.configure(format,null,null,MediaCodec.CONFIGURE_FLAG_ENCODE);  //設置為編碼器
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public void start() throws IOException {
//同樣,在設置錄音開始的時候,也要設置編碼開始
        mEnc.start();

//之前的音頻錄制是直接循環讀取,然后寫入文件,這里需要做編碼處理再寫入文件
//這里的處理就是和之前傳送帶取盒子放原料的流程一樣了,注意一般在子線程中循環處理
        isStopped = false;
        MediaCodec.BufferInfo mInfo=new MediaCodec.BufferInfo();
        FileOutputStream  fos = null;
        TFileHelper.getInstance().deleteFile(recordingFile.getAbsolutePath());
        TFileHelper.getInstance().createFile(recordingFile.getAbsolutePath());

        try {
            fos = new FileOutputStream(recordingFile);
            audioRecorder.start();

            while (!isStopped){     //循環讀取AudioRecorder的數據流
                int index=mEnc.dequeueInputBuffer(-1);
                Log.e(TAG, "start: index1 = " + index);
                if(index>=0){
                    final ByteBuffer buffer=mEnc.getInputBuffer(index);
                    buffer.clear();
                    int length=audioRecorder.getmRecorder().read(buffer,audioRecorder.getBufferSize());
                    Log.e(TAG, "start length = : " + length);
                    if(length>0){
                        mEnc.queueInputBuffer(index,0,length,System.nanoTime()/1000,0);
                    }
                }

                int outIndex;
                //每次取出的時候,把所有加工好的都循環取出來
                do{
                    outIndex=mEnc.dequeueOutputBuffer(mInfo,0);
                    Log.e(TAG, "start: index2 = " + outIndex);
                    if(outIndex>=0){
                        ByteBuffer buffer=mEnc.getOutputBuffer(outIndex);
                        buffer.position(mInfo.offset);
                        //AAC編碼,需要加數據頭,AAC編碼數據頭固定為7個字節
                        byte[] temp=new byte[mInfo.size+7];
                        buffer.get(temp,7,mInfo.size);
                        addADTStoPacket(temp,temp.length);
                        fos.write(temp);
                        mEnc.releaseOutputBuffer(outIndex,false);
                    }else if(outIndex ==MediaCodec.INFO_TRY_AGAIN_LATER){
                        //TODO something
                    }else if(outIndex==MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){
                        //TODO something
                    }
                }while (outIndex>=0);
            }
            //編碼停止,發送編碼結束的標志,循環結束后,停止并釋放編碼器
            mEnc.stop();
            mEnc.release();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (MediaCodec.CryptoException e) {
            e.printStackTrace();
        } finally {
            if(fos != null){
                fos.close();
            }
        }
    }

    /**
     * 給編碼出的aac裸流添加adts頭字段
     * @param packet 要空出前7個字節,否則會搞亂數據
     * @param packetLen
     */
    private void addADTStoPacket(byte[] packet, int packetLen) {
        int profile = 2;  //AAC LC
        int freqIdx = 4;  //44.1KHz
        int chanCfg = 2;  //CPE
        packet[0] = (byte)0xFF;
        packet[1] = (byte)0xF9;
        packet[2] = (byte)(((profile-1)<<6) + (freqIdx<<2) +(chanCfg>>2));
        packet[3] = (byte)(((chanCfg&3)<<6) + (packetLen>>11));
        packet[4] = (byte)((packetLen&0x7FF) >> 3);
        packet[5] = (byte)(((packetLen&7)<<5) + 0x1F);
        packet[6] = (byte)0xFC;
    }

    public void stop(){
        audioRecorder.stop();
        isStopped = true;
    }

}

四、視頻編碼
https://www.jianshu.com/p/f3a55d3d1f5d/ (攝像頭采集的數據格式YUV)

五、MP4文件裁剪

1)mp4parser
https://github.com/sannies/mp4parser

https://blog.csdn.net/u012027644/article/details/53885837

https://blog.csdn.net/u014691453/article/details/53256605

https://blog.csdn.net/foryou96/article/details/64132636 (詳細 )

此開源庫的缺點就是由開源庫裁剪或者合并出來的視頻文件,不能再由此開源庫進行二次操作,否則會拋出異常。

2)FFmepg
https://www.jianshu.com/p/2cf527f2129f

六、視頻壓縮

  1. https://github.com/zerochl/FFMPEG-AAC-264-Android-32-64
  2. https://blog.csdn.net/w690333243/article/details/88591807
  3. https://blog.csdn.net/qq_21937107/article/details/80083380 (根據SilliCompresser改的,部分視頻可能壓縮不成功,有bug。)
    bug:https://stackoverflow.com/questions/36915383/what-does-error-code-1010-in-android-mediacodec-mean

  4. https://www.cnblogs.com/wainiwann/p/4633208.html (MediaMetadataRetriever視頻信息獲?。?/li>
  5. https://blog.csdn.net/chen930724/article/details/50267669 (MediaMetadataRetriever視頻信息獲?。?/li>
  6. https://blog.csdn.net/u014653815/article/details/81084161 (MediaCodec解碼)
  7. https://github.com/mabeijianxi/small-video-record
    https://www.jianshu.com/p/cdae476087d4
    8.https://www.cnblogs.com/wzqnxd/p/10038881.html (SiliCompressor,親測可用,缺點是沒有進度監聽)
    9.https://blog.csdn.net/dzzzheng95/article/details/60142379 (系統相機錄制視頻無效)

七、視頻錄制
調用系統相機,很多API兼容性都不好,如前后攝像頭、輸出路徑,可能都不好使。

  1. https://www.jianshu.com/p/3f4ad878f6c8
  2. https://blog.csdn.net/nature_day/article/details/36889815 (音視頻Uri轉真實地址)

八、音頻

  1. 音頻格式:https://blog.csdn.net/love_xsq/article/details/51254777
  2. 播放音頻的幾種方式:https://www.cnblogs.com/HDK2016/p/8043247.html
  3. 暫停其他應用的音頻:https://blog.csdn.net/franksunny/article/details/12224551
    https://blog.csdn.net/oLevin/article/details/51476122
    https://bbs.csdn.net/topics/391930743?page=1
  4. https://www.cnblogs.com/senior-engineer/p/7867626.html (mediaplayer詳解)
  5. https://blog.csdn.net/weixin_34015860/article/details/87948255 (音視頻播放器框架)
向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女