① android 音視頻01 --- H264的基本原理01
H264壓縮技術主要採用了以下幾種方法對視頻數據進行壓縮。包括:
解決的是空域數據冗餘問題。
解決的是時域數據冗徐問題
將空間上的相關性變為頻域上無關的數據然後進行量化。
經過壓縮後的幀分為:I幀,P幀和B幀:
關鍵幀,採用幀內壓縮技術。
向前參考幀,在壓縮時,只參考前面已經處理的幀。採用幀音壓縮技術。
雙向參考幀,在壓縮時,它即參考前而的幀,又參考它後面的幀。採用幀間壓縮技術。
除了I/P/B幀外,還有圖像序列GOP。
H264的基本原理其實非常簡單,下我們就簡單的描述一下H264壓縮數據的過程。通過攝像頭採集到的視頻幀(按每秒 30 幀算),被送到 H264 編碼器的緩沖區中。編碼器先要為每一幅圖片劃分宏塊。
劃分好宏塊後,計算宏塊的象素值。以此類推,計算一幅圖像中每個宏塊的像素值。
對於視頻數據主要有兩類數據冗餘,一類是時間上的數據冗餘,另一類是空間上的數據冗餘。其中時間上的數據冗餘是最大的。為什麼說時間上的冗餘是最大的呢?假設攝像頭每秒抓取30幀,這30幀的數據大部分情況下都是相關聯的。也有可能不止30幀的的數據,可能幾十幀,上百幀的數據都是關聯特別密切的。
H264編碼器會按順序,每次取出兩幅相鄰的幀進行宏塊比較,計算兩幀的相似度。如下圖:
在H264編碼器中將幀分組後,就要計算幀組內物體的運動矢量了。
H264編碼器首先按順序從緩沖區頭部取出兩幀視頻數據,然後進行宏塊掃描。當發現其中一幅圖片中有物體時,就在另一幅圖的鄰近位置(搜索窗口中)進行搜索。如果此時在另一幅圖中找到該物體,那麼就可以計算出物體的運動矢量了。
運動矢量計算出來後,將相同部分(也就是綠色部分)減去,就得到了補償數據。我們最終只需要將補償數據進行壓縮保存,以後在解碼時就可以恢復原圖了。壓縮補償後的數據只需要記錄很少的一點數據。
我們把運動矢量與補償稱為 幀間壓縮技術 ,它解決的是視頻幀在時間上的數據冗餘。除了幀間壓縮,幀內也要進行數據壓縮,幀內數據壓縮解決的是空間上的數據冗餘。
人眼對圖象都有一個識別度,對低頻的亮度很敏感,對高頻的亮度不太敏感。所以基於一些研究,可以將一幅圖像中人眼不敏感的數據去除掉。這樣就提出了幀內預測技術。
一幅圖像被劃分好宏塊後,對每個宏塊可以進行 9 種模式的預測。找出與原圖最接近的一種預測模式。然後,將原始圖像與幀內預測後的圖像相減得殘差值。再將我們之前得到的預測模式信息一起保存起來,這樣我們就可以在解碼時恢復原圖了,經過幀內與幀間的壓縮後,雖然數據有大幅減少,但還有優化的空間。
可以將殘差數據做整數離散餘弦變換,去掉數據的相關性,進一步壓縮數據。
上面的幀內壓縮是屬於有損壓縮技術。也就是說圖像被壓縮後,無法完全復原。而CABAC屬於無損壓縮技術。
無損壓縮技術大家最熟悉的可能就是哈夫曼編碼了,給高頻的詞一個短碼,給低頻詞一個長碼從而達到數據壓縮的目的。MPEG-2中使用的VLC就是這種演算法,我們以 A-Z 作為例子,A屬於高頻數據,Z屬於低頻數據。看看它是如何做的。
CABAC也是給高頻數據短碼,給低頻數據長碼。同時還會根據上下文相關性進行壓縮,這種方式又比VLC高效很多。
制定了相互傳輸的格式,將宏快 有組織,有結構,有順序的形成一系列的碼流。這種碼流既可 通過 InputStream 網路流的數據進行傳輸,也可以封裝成一個文件進行保存,主要作用是為了傳輸。
組成H264碼流的結構中 包含以下幾部分 ,從大到小排序依次是:
H264視頻序列,圖像,片組,片,NALU,宏塊 ,像素。
NAL層:(Network Abstraction Layer,視頻數據網路抽象層) : 它的作用是H264隻要在網路上傳輸,在傳輸的過程每個包乙太網是1500位元組,而H264的幀往往會大於1500位元組,所以要進行拆包,將一個幀拆成多個包進行傳輸,所有的拆包或者組包都是通過NAL層去處理的。
VCL層:(Video Coding Layer,視頻數據編碼層) : 對視頻原始數據進行壓縮
起始碼0x 00 00 00 01 或者 0x 00 00 01 作為 分隔符 。
兩個 0x 00 00 00 01之間的位元組數據 是表示一個NAL Unit。
I 幀的特點:
1.分組:把幾幀圖像分為一組(GOP,也就是一個序列),為防止運動變化,幀數不宜取多。
2.定義幀:將每組內各幀圖像定義為三種類型,即I幀、B幀和P幀;
3.預測幀:以I幀做為基礎幀,以I幀預測P幀,再由I幀和P幀預測B幀;
4.數據傳輸:最後將I幀數據與預測的差值信息進行存儲和傳輸。
1.更高的編碼效率:同H.263等標準的特率效率相比,能夠平均節省大於50%的碼率。
2.高質量的視頻畫面:H.264能夠在低碼率情況下提供高質量的視頻圖像,在較低帶寬上提供高質量的圖像傳輸是H.264的應用亮點。
3.提高網路適應能力:H.264可以工作在實時通信應用(如視頻會議)低延時模式下,也可以工作在沒有延時的視頻存儲或視頻流伺服器中。
4.採用混合編碼結構:同H.263相同,H.264也使用採用DCT變換編碼加DPCM的差分編碼的混合編碼結構,還增加了如多模式運動估計、幀內預測、多幀預測、基於內容的變長編碼、4x4二維整數變換等新的編碼方式,提高了編碼效率。
5.H.264的編碼選項較少:在H.263中編碼時往往需要設置相當多選項,增加了編碼的難度,而H.264做到了力求簡潔的「回歸基本」,降低了編碼時復雜度。
6.H.264可以應用在不同場合:H.264可以根據不同的環境使用不同的傳輸和播放速率,並且提供了豐富的錯誤處理工具,可以很好的控制或消除丟包和誤碼。
7.錯誤恢復功能:H.264提供了解決網路傳輸包丟失的問題的工具,適用於在高誤碼率傳輸的無線網路中傳輸視頻數據。
8.較高的復雜度:264性能的改進是以增加復雜性為代價而獲得的。據估計,H.264編碼的計算復雜度大約相當於H.263的3倍,解碼復雜度大約相當於H.263的2倍。
H.264的目標應用涵蓋了目前大部分的視頻服務,如有線電視遠程監控、交互媒體、數字電視、視頻會議、視頻點播、流媒體服務等。H.264為解決不同應用中的網路傳輸的差異。定義了兩層:視頻編碼層(VCL:Video Coding Layer)負責高效的視頻內容表示,網路提取層(NAL:Network Abstraction Layer)負責以網路所要求的恰當的方式對數據進行打包和傳送。
② 安卓開發怎麼將和h264文件解碼播放
如題所示,我想將攝像頭採集的數據進行h.264硬編碼,我想知道Android是如何對視頻數據進行硬體編碼的
目前已經知道的方案有:
1、用Android4.1 API MediaCodec來對視頻數據進行編碼
http://stackoverflow.com/q/17232477/2293921
此種方式我測試了,並未成功,目前一直卡在這里,如果你等幫助我,我將非常感激
2、通過MediaRecorder方式對數據進行編碼
具體可參考 http://blog.csdn.net/zblue78/article/details/6083374
3、通過移植ffmpeg
這種方式沒接觸過,也不了解
可能還有一些其他的方式來對視頻硬編碼,如果你了解一下,感謝分享!
綜上,我更傾向於1的方式去做
我來回答
Android , MediaCodec , 硬編碼
post_newreply
//$(\'note_\').focus();
function succeedhandle_vfastpost(url, message, param) {
$(\'vmessage\').value = \'\';
succeedhandle_fastpost(url, message, param);
showCreditPrompt();
}
var vf_tips = \'#在這里快速回復#\';
$(\'vmessage\').value = vf_tips;
$(\'vmessage\').style.color = \'#CDCDCD\';
$(\'vmessage\').onclick = function() {
if($(\'vmessage\').value==vf_tips) {
$(\'vmessage\').value=\'\';
$(\'vmessage\').style.color=\"#000\";
}
}
$(\'vmessage\').onblur = function() {
if(!$(\'vmessage\').value) {
$(\'vmessage\').value=vf_tips;
$(\'vmessage\').style.color=\"#CDCDCD\";
}
}
$(\'vreplysubmit\').onclick = function() {
if($(\'vmessage\').value == vf_tips) {
return false;
}
}
③ android h264硬編碼,得到流寫入文件後不能播放是怎麼回事
自己寫解碼264文件,如果用view顯示,就需要轉成bitmap顯示,或者使用opengl可以顯示yuv數據
如果已經保存成MP4格式的文件,就不需要解碼了,通過mediaplayer就能播
④ Android自帶的瀏覽器是否支持h264編碼的html5視頻
特別是移動平台的瀏覽器對h264的支持尤其重要,近兩年的ARM處理器基本都包含h264硬解碼,若不啟用硬解碼,不但耗電,流暢性也得不到保證。
⑤ Android音頻開發(三)——音頻編解碼
上一節中我們講了怎麼採集音頻並播放,由於AudioRecord採集的是PCM數據,沒有經過處理,所有播放的時候會有雜音,嘯叫等現象出現。因此處理掉這些不需要的數據就是本節的內容,編碼與解碼。
Android官方提供給我們的用於編解碼的類是 MediaCodec ,它是android 4.1(API 16)才引入的,所以只能工作於andorid4.1以上的手機,如果想兼容4.1以下版本的手機,只能使用第三方庫,如大名鼎鼎的 ffmpeg ,B站的 ijkplayer 等。
(1)提供了一套訪問 Android 底層多媒體模塊的介面,主要是音視頻的編解碼介面
(2)在Android上,預設的多媒體框架是基於第三方PacketVideo公司的OpenCORE來實現,OpenCORE的優點是兼顧了跨平台的移植性,而且已經過多方驗證,所以相對來說較為穩定;缺點是國語龐大復雜,需要耗費相當多的時間去維護。因此從Android 2.0開始,Google引進了較為簡潔的StageFright。Android 底層多媒體模塊採用的是 StageFright 框架,它是基於OpenMax標准實現的,任何 Android 底層編解碼模塊的實現,都必須遵循 OpenMax 標准。值得一提的是,OpenMAX是Khronos制定的API,Khronos也是OpenGL的制定者。Google 官方默認提供了一系列的軟體編解碼器:包括:OMX.google.h264.encoder,OMX.google.h264.encoder, OMX.google.aac.encoder, OMX.google.aac.decoder 等等,而硬體編解碼功能,則需要由晶元廠商依照 OpenMax 框架標准來完成,所以,一般採用不同晶元型號的手機,硬體編解碼的實現和性能是不同的
(3)Android 應用層統一由 MediaCodec API 來提供各種音視頻編解碼功能,由參數配置來決定採用何種編解碼演算法、是否採用硬體編解碼加速等等
根據android官方文檔的描述,MediaCodec的核心就是使用緩沖區隊列來操作數據,使用流程如下:
//name既是媒體文件的類型,如audio/3gpp,詳情參考MediaFormat的MIMETYPE常量
MediaCodec codec = MediaCodec.createByCodecName(name);
codec.configure(format, …);
MediaFormat outputFormat = codec.getOutputFormat(); // option B
codec.start();
for (;;) {
////獲取可用的inputBuffer -1代表一直等待,0表示不等待 建議-1,避免丟幀
int inputBufferId = codec.dequeueInputBuffer(-1);
if (inputBufferId >= 0) {
ByteBuffer inputBuffer = codec.getInputBuffer(…);
// fill inputBuffer with valid data
…
codec.queueInputBuffer(inputBufferId, …);
}
//執行上面的操作後就把待編解碼的數據存入了輸入緩沖區,然後下一步就是操作然後把編解碼的數據存入輸出緩沖區
int outputBufferId = codec.dequeueOutputBuffer(…);
if (outputBufferId >= 0) {
ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A
// bufferFormat is identical to outputFormat
// outputBuffer is ready to be processed or rendered.
…
codec.releaseOutputBuffer(outputBufferId, …);
} else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// Subsequent data will conform to new format.
// Can ignore if using getOutputFormat(outputBufferId)
outputFormat = codec.getOutputFormat(); // option B
}
}
codec.stop();
codec.release();
MediaCodec codec = MediaCodec.createByCodecName(name);
MediaFormat mOutputFormat; // member variable
codec.setCallback(new MediaCodec.Callback() {
@Override
void onInputBufferAvailable(MediaCodec mc, int inputBufferId) {
ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferId);
// fill inputBuffer with valid data
…
codec.queueInputBuffer(inputBufferId, …);
}
@Override
void onOutputBufferAvailable(MediaCodec mc, int outputBufferId, …) {
ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A
// bufferFormat is equivalent to mOutputFormat
// outputBuffer is ready to be processed or rendered.
…
codec.releaseOutputBuffer(outputBufferId, …);
}
@Override
void onOutputFormatChanged(MediaCodec mc, MediaFormat format) {
// Subsequent data will conform to new format.
// Can ignore if using getOutputFormat(outputBufferId)
mOutputFormat = format; // option B
}
@Override
void onError(…) {
…
}
});
codec.configure(format, …);
mOutputFormat = codec.getOutputFormat(); // option B
codec.start();
// wait for processing to complete
codec.stop();
codec.release();
MediaCodec codec = MediaCodec.createByCodecName(name);
codec.configure(format, …);
codec.start();
//API的區別在這里
ByteBuffer[] inputBuffers = codec.getInputBuffers();
ByteBuffer[] outputBuffers = codec.getOutputBuffers();
for (;;) {
int inputBufferId = codec.dequeueInputBuffer(…);
if (inputBufferId >= 0) {
// fill inputBuffers[inputBufferId] with valid data
…
codec.queueInputBuffer(inputBufferId, …);
}
int outputBufferId = codec.dequeueOutputBuffer(…);
if (outputBufferId >= 0) {
// outputBuffers[outputBufferId] is ready to be processed or rendered.
…
codec.releaseOutputBuffer(outputBufferId, …);
} else if (outputBufferId == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
outputBuffers = codec.getOutputBuffers();
} else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// Subsequent data will conform to new format.
MediaFormat format = codec.getOutputFormat();
}
}
codec.stop();
codec.release();
⑥ Android平台FFmpeg實現rtmp推流-C++的實現
視頻編碼有幾種方式:
1.硬編碼,使用MediaCodec實現
2.軟編碼,使用FFmpeg或者libx264庫來實現。
本文分享在Android平台視頻編碼-軟編碼的實現,也就是用FFmpeg來實現視頻的編碼,rtmp推流到伺服器上,相機採集視頻將在下一篇文章分享。
流媒體伺服器使用 nginx-rtmp-mole 來進行搭建。
本文所使用FFmpeg的版本是4.1,關於FFmpeg編譯成Android平台so庫如果有需要,我將在下一篇文章分享說明。
視頻編碼比較耗cpu,上傳視頻數據的會耗網路io,所以需要開啟新線程去處理,這里我用HandlerThread來處理視頻的編碼上傳。
初始化編碼相關操作
這里我們使用的是FFmpeg,所以在編碼前我們會先做一些初始化以及參數設置工作。
FFmpeg初始化
av_register_all()
創建輸出格式上下文
avformat_alloc_output_context2()
獲取編碼器
avcodec_find_encoder(AV_CODEC_ID_H264) 獲取H264的編碼器
設置編碼器參數
使用給定的編碼器和參數初始化編碼上下文
avcodec_open2(pCodecCtx, pCodec, ¶m)
創建視頻流
video_st = avformat_new_stream(ofmt_ctx, pCodec)
打開輸出上下文
avio_open(&ofmt_ctx->pb, out_path, AVIO_FLAG_READ_WRITE)
寫入輸出頭信息
avformat_write_header(ofmt_ctx, NULL)
像素格式轉換
AV_PIX_FMT_YUV420P,它是純平面存儲。總共三個平面,分別存放,Y、U、V數據。
當圖像寬是width,高是height時,Y分量的大小就是width×heitht,而U是width×heitht/4,V也是U是width×heitht/4。
H264編碼
首先我們需要了解兩個數據結構AVFrame、AVPacket
AVFrame存放的是原始數據、AVPacket存放的是編碼後的數據。
創建AVPacket
av_new_packet(&enc_pkt, picture_size);
開始編碼
ret = avcodec_encode_video2(pCodecCtx, pFrameYUV);
輸出一幀編碼後的視頻數據
ret = av_write_frame(pCodecCtx, &enc_pkt);
釋放資源
小夥伴們有疑問的可以在下方評論區評論。
⑦ android mediarecorder硬編碼獲取音視頻流,怎樣設置時間戳達到音視頻同步
參考spydroid開源項目,這個開源項目有音頻(aac),視頻(H264)流的讀取,具體操作都有,就是音視頻時間戳不知怎麼弄,音視頻同步搞不定,您有想法記得分享哦!
⑧ android開發怎麼把視頻編碼成H265
首先,你的文件有沒有錯誤。 比如,拿出來,放在pc上看看是否能播。不能播,可能你保存的文件有誤。如果,能播。那麼可能與文件的許可權有問題。如果放在file的目錄下,可能需要更改文件目錄及創建的文件的許可權。其方法網上有,調用java的方法,使用linux命令行。
⑨ Android音視頻開發——H264的基本概念
ffmpeg常用命令
封裝格式 。
編碼的本質就是壓縮數據
音頻編碼的作用: 將音頻采樣數據( PCM 等)壓縮成音頻碼流,從而降低音頻的數據量。 常用的音頻編碼方式有以下幾種:
H264壓縮技術主要採用了以下幾種方法對視頻數據進行壓縮。包括:
經過壓縮後的幀分為:I幀,P幀和B幀:
除了I/P/B幀外,還有圖像序列GOP。
組成碼流的結構中,包含了以下幾個部分,從大到小依次是:
H264視頻序列,圖像,片組,片,NALU,宏塊,像素
H264功能分為兩層:
1.H264視頻序列包括一系列的NAL單元,每個NAL單元包含一個RBSP。
2.一個原始的H.264由 N個NALU單元組成
3.NALU單元由[StartCode][NALU Header][NALU Payload]三部分組成
5.NAL Header
由三部分組成forbidden_bit(1bit)(禁止位),nal_reference_bit(2bits)(優先順序,,值越大,該NAL越重要),nal_unit_type(5bits)(類型)
nal_unit_type
6.NAL的解碼單元的流程如下