国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【音視頻筆記】Mediacodec+Muxer生成mp4,瀏覽器無法播放問題處理

這篇具有很好參考價值的文章主要介紹了【音視頻筆記】Mediacodec+Muxer生成mp4,瀏覽器無法播放問題處理。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

背景

最近在測試視頻錄制功能時發(fā)現,AudioRecord + MediaCodec + MediaMuxer生成的MP4,PC瀏覽器無法播放 ,但是Android、Windows、Mac的播放器應用都能正常播放。雖然不禁想吐槽瀏覽器視頻組件的容錯性差,但我也意識生成的文件格式肯定也是有問題的。

然后嘗試了合成MP4視頻時,只保留視頻通道,不要音頻,發(fā)現拖到瀏覽器中可以正常播放。使用ffprobe檢查有問題的MP4文件,有如下錯誤輸出:

[aac @ 0x7f95c9c0e7c0] Input buffer exhausted before END element found

至此,基本確定問題出現在生成的音頻數據上。

解決過程

由于此前個人音視頻開發(fā)經驗不足,MediaCodec、MediaMuxer編碼和合成視頻的相關代碼參考了一些開源項目及博客。
但由于開發(fā)周期緊急,沒有足夠的時間來仔細研究和排查,當時就采用了一種曲線救國的方案。

曲線修復方案

能想到這個方案也比較偶然。當時查閱了一些資料和博客,用到了ffmpegffprobe工具對問題視頻進行分析。

在嘗試了使用ffmpeg工具對問題視頻進行轉換后,意外地發(fā)現,雖然命令也會報錯[aac @ 0x7f95c9c0e7c0] Input buffer exhausted before END element found,但是,問題視頻經過fmpeg轉換后,生成的新視頻,用ffprobe命令查看是沒有錯誤輸出的,也可以正常播放!也就是說,ffmpeg在處理轉換有問題的音頻時,會自動跳過那些有問題的數據。

由此,想到了一個比較曲折的方案:先用AudioRecord + MediaCodec + MediaMuxer生成MP4,然后使用ffmpeg命令對生成的視頻進行一點無關緊要的轉換(重點是讓它處理掉有問題的數據),然后就能得到一個格式正確的音頻數據,然后用MediaExtractor提取出原MP4中的視頻數據,最后用MediaMuxer合成最終格式正確的mp4文件。
因為是音頻有問題,所以實踐中我就使用了如下命令來轉換:

ffmpeg -i input.mp4 -vn -ab 96k out.m4a

-vn參數指定不要視頻數據,-ab 96k將音頻碼率轉為96k。

現在,只需要裁剪、交叉編譯一個滿足以上需求的arm版本的ffmpeg可執(zhí)行程序就好了。關于如何裁剪和編譯ffmpeg,網上音視頻相關的技術文章一大把,就不贅述細節(jié)了。

這里記錄一下我反復測試編譯配置參數后,能輸出較小體積(約2.6MB)arm版ffmpeg可執(zhí)行命令的編譯腳本,方便以后查看。因為我只需要處理音頻,所以這個配置編譯出的ffmpeg只能解碼MP4和aac,并且只支持輸出m4a音頻。

#!/bin/sh

# NDK路徑,根據電腦環(huán)境配置情況調整
NDK_HOME="/Users/shenyong/Library/Android/sdk/ndk/21.4.7075529"
TOOLCHAIN="$NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64"
SYSROOT="$TOOLCHAIN/sysroot"

# 默認使用arm編譯配置
API=29
ARCH=arm
CPU=armv7-a
TOOL_CPU_NAME=armv7a
# CROSS_PREFIX, CC and CXX for arm
CROSS_PREFIX="$TOOLCHAIN/bin/arm-linux-androideabi-"
CC="$TOOLCHAIN/bin/$TOOL_CPU_NAME-linux-androideabi$API-clang"
CXX="$TOOLCHAIN/bin/$TOOL_CPU_NAME-linux-androideabi$API-clang++"
OUTPUT_DIR="./android/$CPU"
OPTIMIZE_CFLAGS="-march=$CPU"

function config_arm64() {
  ARCH=arm64
  CPU=armv8-a
  TOOL_CPU_NAME=aarch64
  # CROSS_PREFIX, CC and CXX for arm64
  CROSS_PREFIX="$TOOLCHAIN/bin/$TOOL_CPU_NAME-linux-android-"
  CC="$TOOLCHAIN/bin/$TOOL_CPU_NAME-linux-android$API-clang"
  CXX="$TOOLCHAIN/bin/$TOOL_CPU_NAME-linux-android$API-clang++"
  OUTPUT_DIR="./android/$CPU"
  OPTIMIZE_CFLAGS="-march=$CPU"

  #libmediandk.so路徑
  MEDIA_NDK_LIB=$TOOLCHAIN/sysroot/usr/lib/aarch64-linux-android/$API
  ADD_MEDIA_NDK_SO="--extra-ldflags=-L$MEDIA_NDK_LIB --extra-libs=-lmediandk "
}

# 如果需要編譯arm64版本,將以下行取消注釋即可
config_arm64

#清除之前的編譯配置及輸出
make distclean

./configure \
--prefix=$OUTPUT_DIR \
--target-os=android \
--arch=$ARCH \
--cpu=$CPU \
--enable-cross-compile \
--cross-prefix=$CROSS_PREFIX \
--sysroot=$SYSROOT \
--cc=$CC \
--cxx=$CXX \
--extra-cflags="-Os -fpic $OPTIMIZE_CFLAGS " \
--disable-shared \
--enable-static \
--enable-neon \
--disable-asm \
--disable-gpl \
--disable-postproc \
--enable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-avdevice \
--disable-doc \
--disable-symver \
--disable-protocols \
--enable-protocol=file \
--disable-network \
--disable-jni \
--disable-mediacodec \
--disable-hwaccels \
--disable-encoders \
--enable-encoder=aac \
--disable-decoders \
--enable-decoder=aac \
--enable-decoder=mpeg4 \
--disable-muxers \
--enable-muxer=ipod \
--disable-demuxers \
--enable-demuxer=aac \
--enable-demuxer=mpegvideo \
--enable-demuxer=mov \
--disable-parsers \
--enable-parser=aac \
--enable-parser=mpeg4video \
--enable-parser=mpegaudio \
--disable-filters \
--disable-bsfs \
--enable-bsf=aac_adtstoasc

make clean
make -j12
make install

解決問題根源

既然自己分析找不到問題根源,就看看別人正常工作的代碼有什么不一樣吧,于是開始在GitHub上找相似功能的開源庫。在運行AudioVideoRecordingSample這個演示庫后,發(fā)現別人生成的視頻和音頻,用ffprobe命令檢查格式都是正確的。

仔細分析對比后,終于找到了問題點。網上各種博客的示例代碼中,都是在dequeueOutputBuffer()返回的輸出buffer下標大于0時,就直接寫入Muxer,關鍵部分類似這樣:

int outputBufferIndex = mAudioCodec.dequeueOutputBuffer(bufferInfo, 0);
if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
    // 將mMediaCodec的指定的格式的數據軌道,設置到mMediaMuxer上
    mAudioTrackIndex = mMediaMuxer.addTrack(mAudioCodec.getOutputFormat());
	// ...
} else {
    while (outputBufferIndex >= 0) {
        // 獲取數據
        ByteBuffer outBuffer = mAudioCodec.getOutputBuffers()[outputBufferIndex];
        audioPts = (System.nanoTime() - startNanoTime) / 1000;
        bufferInfo.presentationTimeUs = audioPts;
        // 編碼數據寫入muxer
        mMediaMuxer.writeSampleData(mAudioTrackIndex, outBuffer, bufferInfo);
        // 釋放 outBuffer
        mAudioCodec.releaseOutputBuffer(outputBufferIndex, false);

        outputBufferIndex = mAudioCodec.dequeueOutputBuffer(bufferInfo, 0);
    }
}

但是,我發(fā)現AudioVideoRecordingSample這個庫在獲取的outputBufferIndex >= 0時,還有一個關鍵的處理:

// ...
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
    // You shoud set output format to muxer here when you target Android4.3 or less
    // but MediaCodec#getOutputFormat can not call here(because INFO_OUTPUT_FORMAT_CHANGED don't come yet)
    // therefor we should expand and prepare output format from buffer data.
    // This sample is for API>=18(>=Android 4.3), just ignore this flag here
    if (DEBUG) Log.d(TAG, "drain:BUFFER_FLAG_CODEC_CONFIG");
    mBufferInfo.size = 0;
}
if (mBufferInfo.size != 0) {
    // ...
    muxer.writeSampleData(mTrackIndex, encodedData, mBufferInfo);
}
/// ...

就是判斷當前的mBufferInfo有BUFFER_FLAG_CODEC_CONFIG這個標志時,把size置為0了,所以這一次回調的數據,是沒有寫入muxer的。于是趕緊看了一眼BUFFER_FLAG_CODEC_CONFIG的官方文檔:

    /**
     * This indicated that the buffer marked as such contains codec
     * initialization / codec specific data instead of media data.
     */
    public static final int BUFFER_FLAG_CODEC_CONFIG = 2;

這才恍然大悟!當BufferInfo有這個標志的時候,buffer包含編解碼器初始化或編解碼器特定的數據而不是媒體數據!

于是在自己的代碼中也上這個判斷處理,生成的視頻文件再用ffprobe查看,也能正常輸出信息,沒有報錯了。關鍵代碼如下:

while (true) {
    try {
        // 返回有效數據填充的輸出緩沖區(qū)的索引
        int outputBufferIndex = mAudioCodec.dequeueOutputBuffer(bufferInfo, 0);
        if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
            // 將mMediaCodec的指定的格式的數據軌道,設置到mMediaMuxer上
            mAudioTrackIndex = mMediaMuxer.addTrack(mAudioCodec.getOutputFormat());
        } else {
            while (outputBufferIndex >= 0) {
                // 獲取數據
                ByteBuffer outBuffer = mAudioCodec.getOutputBuffers()[outputBufferIndex];
                // 修改音頻的 pts,基準時間戳
                audioPts = (System.nanoTime() - startNanoTime) / 1000;
                bufferInfo.presentationTimeUs = audioPts;
                if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
                    Log.w(TAG, "audio BUFFER_FLAG_CODEC_CONFIG bufferInfo.size: " + bufferInfo.size);
                    // 配置回調,不是有效的媒體數據,不寫入。如果寫入了,會導致mp4文件有錯誤數據幀,
                    // 容錯性不夠好的播放器(比如pc瀏覽器)可能無法正常播放視頻。
                      bufferInfo.size = 0;
                }
                // 寫入音頻數據
                if (bufferInfo.size > 0) {
                    mMediaMuxer.writeSampleData(mAudioTrackIndex, outBuffer, bufferInfo);
                }
                if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                    Log.w(TAG, "audio BUFFER_FLAG_END_OF_STREAM bufferInfo.size: " + bufferInfo.size);
                }
                // 釋放 outBuffer
                mAudioCodec.releaseOutputBuffer(outputBufferIndex, false);
                if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                    Log.w(TAG, "audio got BUFFER_FLAG_END_OF_STREAM flag. audioPts: "
                            + bufferInfo.presentationTimeUs + "bufferInfo.size: " + bufferInfo.size);
                    if (shouldExit) {
                        onDestroy();
                        return;
                    }
                }
                outputBufferIndex = mAudioCodec.dequeueOutputBuffer(bufferInfo, 0);
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

這樣一來,使用Mediacodec+Muxer就能生成格式正確的mp4視頻文件了,無需其他處理,效率大大提高。

從打印日志來看,帶這個標志的一般就是第一個輸出的buffer,并且數據量很少:

2023-09-21 10:16:36.664 BaseVid...corder  W  audio BUFFER_FLAG_CODEC_CONFIG bufferInfo.size: 2
2023-09-21 10:16:36.675 BaseVid...corder  W  video BUFFER_FLAG_CODEC_CONFIG bufferInfo.size: 30

最后經過測試驗證,也確實是這樣的:
只要bufferInfo有BUFFER_FLAG_CODEC_CONFIG標志時,把buffer數據寫入muxer了,用ffprobe查看生成的視頻文件,就一定會有[aac @ 0x7f95c9c0e7c0] Input buffer exhausted before END element found這個錯誤輸入;反之不寫入就是正常的。文章來源地址http://www.zghlxwxcb.cn/news/detail-730869.html

到了這里,關于【音視頻筆記】Mediacodec+Muxer生成mp4,瀏覽器無法播放問題處理的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 【音視頻開發(fā)】FFmpeg轉換與封裝 I - MP4格式

    【音視頻開發(fā)】FFmpeg轉換與封裝 I - MP4格式

    1 FFmpeg轉換與封裝 1.1 MP4格式轉換 1.1.1 MP4格式標準 ????????FFmpeg支持的媒體封裝格式具有多樣性與全面性,與此, 我們還可以使用FFmpeg來對媒體格式進行轉換與封裝 。 在互聯網常見的格式中,跨平臺最好的應該是 MP4 文件,因為 MP4 文件既可以在PC 平臺的Flashplayer中播放,

    2024年02月08日
    瀏覽(34)
  • 用ffmpeg解析mp4文件得到時長、比特率、音視頻信息

    以下是使用C++語言調用FFmpeg獲取視頻流和音頻流信息的示例代碼: 上述代碼通過 AVFormatContext 結構體和FFmpeg庫函數 avformat_open_input 、 avformat_find_stream_info 等,獲取MP4文件的視頻流和音頻流信息,并將結果存儲到 MediaInfo 類中。在實際應用中,可以將上述代碼封裝成一個函數,

    2024年02月12日
    瀏覽(96)
  • 音視頻知識:MPEG-4、H264、MP4、AAC之間的關系

    MPEG-4 一種編碼標準。是國際標準化組織 (ISO) 主要針對消費類應用,已經針對運動圖像壓縮定義的標準。MPEG(Moving Picture Experts Group)標準包括 MPEG1、MPEG2與 MPEG4。 MPEG-4標準目前分為27個部分,統稱為ISO/IEC14496國際標準。其中第10部分(ISO/IEC 14496-10)就是熟悉的高級視頻編碼

    2024年02月14日
    瀏覽(40)
  • Qt音視頻開發(fā)47-文字和圖片水?。纱鎯Φ組P4中)

    近期花了兩周時間閉門啃硬骨頭,主要就解決三個問題(音視頻同步存儲和推流、圖片水印并將水印信息存儲到文件或者推流、rtsp推流),這三個問題困擾了很多年,以至于找遍了網絡和翻遍ffplay代碼以及ffmpeg示例的代碼,通過不下于上百次方案的調整和測試,幾乎每次都是

    2024年02月16日
    瀏覽(42)
  • JavaScript音視頻,使用JavaScript如何在瀏覽器錄制電腦攝像頭畫面為MP4視頻文件并下載視頻文件到本地

    本章介紹使用JavaScript如何在瀏覽器錄制電腦攝像頭畫面為MP4視頻文件并下載視頻文件到本地。 1、使用navigator.mediaDevices.getUserMedia獲取攝像頭畫面 2、將獲取到的攝像頭畫面渲染到canvas畫板上 3、將canvas轉換為blob對象 4、通過document.createElement(‘a’)調用 href 方法獲取此鏈接并觸

    2024年02月02日
    瀏覽(31)
  • 「Python|音視頻處理|場景案例」如何使用ffmpeg下載m3u8視頻到本地并保存成mp4

    本文主要介紹如何使用ffmpeg批量下載視頻到本地并保存成指定格式。 當我們希望將網頁上的視頻下載到本地的時候,我們可能獲取到的視頻地址是指向 .m3u8 格式的,當視頻多的時候,手動下載并使用工具轉換格式就顯得工作量過大。 ffmpeg 是一個強大的音視頻處理工具,具體

    2024年02月16日
    瀏覽(34)
  • Qt/C++音視頻開發(fā)69-保存監(jiān)控pcm音頻數據到mp4文件/監(jiān)控錄像/錄像存儲和回放/264/265/aac/pcm等

    用ffmpeg做音視頻保存到mp4文件,都會遇到一個問題,尤其是在視頻監(jiān)控行業(yè),就是監(jiān)控攝像頭設置的音頻是PCM/G711A/G711U,解碼后對應的格式是pcm_s16be/pcm_alaw/pcm_mulaw,將這個原始的音頻流保存到mp4文件是會報錯的,在調用avformat_write_header寫文件頭的時候提示(-22) Invalid argument,

    2024年04月11日
    瀏覽(37)
  • Android音視頻-MediaCodec

    Android音視頻-MediaCodec

    原文:https://mp.weixin.qq.com/s?__biz=MzU3NTA3MDU1OQ==mid=2247484865idx=1sn=174b8ca702466e83e72c7115d91b06eachksm=fd298df1ca5e04e7b2df9dc9f21e5cfe3e910204c905d8605f648ce6f6404432a83ae52a23a3scene=178cur_album_id=1638784435628064770#rd MediaCodec 支持處理三種數據類型,分別是壓縮數據(compressed data)、原始音頻數據(raw audio d

    2023年04月08日
    瀏覽(16)
  • ffmpeg把RTSP流分段錄制成MP4,如果能把ffmpeg.exe改成ffmpeg.dll用,那音視頻開發(fā)的難度直接就降一個維度啊

    ffmpeg把RTSP流分段錄制成MP4,如果能把ffmpeg.exe改成ffmpeg.dll用,那音視頻開發(fā)的難度直接就降一個維度啊

    比如,原來我們要用ffmpeg錄一段RTSP視頻流轉成MP4,我們有兩種方案: 方案一:可以使用以下命令將rtsp流分段存儲為mp4文件 ffmpeg -i rtsp://example.com/stream -vcodec copy -acodec aac -f segment -segment_time 3600 -reset_timestamps 1 -strftime 1 output_%Y-%m-%d_%H-%M-%S.mp4 方案二:可以直接調用ffmpeg庫avcode

    2024年02月10日
    瀏覽(36)
  • Android 音視頻入門 (四)- 記錄一次MediaCodec ,天吶

    Android 音視頻入門 (四)- 記錄一次MediaCodec ,天吶

    isEncoding = true //開始編碼 mMediaCodec.start() //構建連接器。 mWorkerThread = HandlerThread(“WorkerThread-Encoder”) mWorkerThread.start() mHandler = Handler(mWorkerThread.looper) } 注意,我們并不在此處就開啟Muxer,我們會在子線程中接受數據的時候的某個狀態(tài)開始進行混合。 mCameraDevice.setPreviewCallback {

    2024年04月16日
    瀏覽(22)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包