最簡單的基於FFmpeg的封裝格式處理:視音訊分離器簡化版(demuxer-simple)
=====================================================
最簡單的基於FFmpeg的封裝格式處理系列文章列表:
=====================================================
簡介
打算記錄一下基於FFmpeg的封裝格式處理方面的例子。包括了視音訊分離,複用,封裝格式轉換。有關封轉格式轉換的例子在之前的文章:《最簡單的基於FFMPEG的封裝格式轉換器(無編解碼)》中已經有過記錄,不再重複。因此計劃寫3篇文章分別記錄視音訊的複用器(Muxer)和分離器(Demuxer)。其中視音訊分離器(Demuxer)記錄2篇:一篇簡單的,一篇標準的。簡單的版本更適合初學者學習。
本文是第1篇。首先記錄一個基於FFmpeg的視音訊分離器簡單版(Simplest FFmpeg Demuxer Simple)。視音訊分離器(Demuxer)即是將封裝格式資料(例如MKV)中的視訊壓縮資料(例如H.264)和音訊壓縮資料(例如AAC)分離開。如圖所示。在這個過程中並不涉及到編碼和解碼。
本文記錄的程式將一個FLV封裝的檔案(其中視訊編碼為H.264,音訊編碼為MP3)分離成為兩個檔案:一個H.264編碼的視訊碼流檔案,一個MP3編碼的音訊碼流檔案。需要注意的是,本文介紹的是一個簡單版的視音訊分離器(Demuxer)。該分離器的優點是程式碼十分簡單,很好理解。但是缺點是並不適用於一些格式。對於MP3編碼的音訊是沒有問題的。但是在分離MP4/FLV/MKV等一些格式中的AAC編碼的碼流的時候,得到的AAC碼流是不能播放的。原因是儲存AAC資料的AVPacket的data欄位中的資料是不包含7位元組ADTS檔案頭的“砍頭”的資料,是無法直接解碼播放的(當然如果在這些資料前面手工加上7位元組的ADTS檔案頭的話,就可以播放了)。
分離某些封裝格式中的H.264
分離某些封裝格式(例如MP4/FLV/MKV等)中的H.264的時候,需要首先寫入SPS和PPS,否則會導致分離出來的資料沒有SPS、PPS而無法播放。H.264碼流的SPS和PPS資訊儲存在AVCodecContext結構體的extradata中。需要使用ffmpeg中名稱為“h264_mp4toannexb”的bitstream filter處理。有兩種處理方式:
(1)使用bitstream filter處理每個AVPacket(簡單)
把每個AVPacket中的資料(data欄位)經過bitstream filter“過濾”一遍。關鍵函式是av_bitstream_filter_filter()。示例程式碼如下。
AVBitStreamFilterContext* h264bsfc = av_bitstream_filter_init("h264_mp4toannexb");
while(av_read_frame(ifmt_ctx, &pkt)>=0){
if(pkt.stream_index==videoindex){
av_bitstream_filter_filter(h264bsfc, ifmt_ctx->streams[videoindex]->codec, NULL, &pkt.data, &pkt.size, pkt.data, pkt.size, 0);
fwrite(pkt.data,1,pkt.size,fp_video);
//...
}
av_free_packet(&pkt);
}
av_bitstream_filter_close(h264bsfc);
上述程式碼中,把av_bitstream_filter_filter()的輸入資料和輸出資料(分別對應第4,5,6,7個引數)都設定成AVPacket的data欄位就可以了。
需要注意的是bitstream filter需要初始化和銷燬,分別通過函式av_bitstream_filter_init()和av_bitstream_filter_close()。
經過上述程式碼處理之後,AVPacket中的資料有如下變化:
*每個AVPacket的data添加了H.264的NALU的起始碼{0,0,0,1}
*每個IDR幀資料前面添加了SPS和PPS
(2)手工新增SPS,PPS(稍微複雜)
將AVCodecContext的extradata資料經過bitstream filter處理之後得到SPS、PPS,拷貝至每個IDR幀之前。下面程式碼示例了寫入SPS、PPS的過程。
FILE *fp=fopen("test.264","ab");
AVCodecContext *pCodecCtx=...
unsigned char *dummy=NULL;
int dummy_len;
AVBitStreamFilterContext* bsfc = av_bitstream_filter_init("h264_mp4toannexb");
av_bitstream_filter_filter(bsfc, pCodecCtx, NULL, &dummy, &dummy_len, NULL, 0, 0);
fwrite(pCodecCtx->extradata,pCodecCtx-->extradata_size,1,fp);
av_bitstream_filter_close(bsfc);
free(dummy);
然後修改AVPacket的data。把前4個位元組改為起始碼。示例程式碼如下所示。char nal_start[]={0,0,0,1};
memcpy(packet->data,nal_start,4);
經過上述兩步也可以得到可以播放的H.264碼流,相對於第一種方法來說複雜一些。參考文章:使用FFMPEG類庫分離出多媒體檔案中的H.264碼流當封裝格式為MPEG2TS的時候,不存在上述問題。
流程
程式的流程如下圖所示。從流程圖中可以看出,將每個通過av_read_frame()獲得的AVPacket中的資料直接寫入檔案即可。簡單介紹一下流程中各個重要函式的意義:
avformat_open_input():開啟輸入檔案。
av_read_frame():獲取一個AVPacket。
fwrite():根據得到的AVPacket的型別不同,分別寫入到不同的檔案中。
程式碼
下面貼上程式碼:
/**
* 最簡單的基於FFmpeg的視音訊分離器(簡化版)
* Simplest FFmpeg Demuxer Simple
*
* 雷霄驊 Lei Xiaohua
* [email protected]
* 中國傳媒大學/數字電視技術
* Communication University of China / Digital TV Technology
* http://blog.csdn.net/leixiaohua1020
*
* 本程式可以將封裝格式中的視訊碼流資料和音訊碼流資料分離出來。
* 在該例子中, 將FLV的檔案分離得到H.264視訊碼流檔案和MP3
* 音訊碼流檔案。
*
* 注意:
* 這個是簡化版的視音訊分離器。與原版的不同在於,沒有初始化輸出
* 視訊流和音訊流的AVFormatContext。而是直接將解碼後的得到的
* AVPacket中的的資料通過fwrite()寫入檔案。這樣做的好處是流程比
* 較簡單。壞處是對一些格式的視音訊碼流是不適用的,比如說
* FLV/MP4/MKV等格式中的AAC碼流(上述封裝格式中的AAC的AVPacket中
* 的資料缺失了7位元組的ADTS檔案頭)。
*
*
* This software split a media file (in Container such as
* MKV, FLV, AVI...) to video and audio bitstream.
* In this example, it demux a FLV file to H.264 bitstream
* and MP3 bitstream.
* Note:
* This is a simple version of "Simplest FFmpeg Demuxer". It is
* more simple because it doesn't init Output Video/Audio stream's
* AVFormatContext. It write AVPacket's data to files directly.
* The advantages of this method is simple. The disadvantages of
* this method is it's not suitable for some kind of bitstreams. For
* example, AAC bitstream in FLV/MP4/MKV Container Format(data in
* AVPacket lack of 7 bytes of ADTS header).
*
*/
#include <stdio.h>
#define __STDC_CONSTANT_MACROS
#ifdef _WIN32
//Windows
extern "C"
{
#include "libavformat/avformat.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavformat/avformat.h>
#ifdef __cplusplus
};
#endif
#endif
//'1': Use H.264 Bitstream Filter
#define USE_H264BSF 1
int main(int argc, char* argv[])
{
AVFormatContext *ifmt_ctx = NULL;
AVPacket pkt;
int ret, i;
int videoindex=-1,audioindex=-1;
const char *in_filename = "cuc_ieschool.flv";//Input file URL
const char *out_filename_v = "cuc_ieschool.h264";//Output file URL
const char *out_filename_a = "cuc_ieschool.mp3";
av_register_all();
//Input
if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
printf( "Could not open input file.");
return -1;
}
if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
printf( "Failed to retrieve input stream information");
return -1;
}
videoindex=-1;
for(i=0; i<ifmt_ctx->nb_streams; i++) {
if(ifmt_ctx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){
videoindex=i;
}else if(ifmt_ctx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO){
audioindex=i;
}
}
//Dump Format------------------
printf("\nInput Video===========================\n");
av_dump_format(ifmt_ctx, 0, in_filename, 0);
printf("\n======================================\n");
FILE *fp_audio=fopen(out_filename_a,"wb+");
FILE *fp_video=fopen(out_filename_v,"wb+");
/*
FIX: H.264 in some container format (FLV, MP4, MKV etc.) need
"h264_mp4toannexb" bitstream filter (BSF)
*Add SPS,PPS in front of IDR frame
*Add start code ("0,0,0,1") in front of NALU
H.264 in some container (MPEG2TS) don't need this BSF.
*/
#if USE_H264BSF
AVBitStreamFilterContext* h264bsfc = av_bitstream_filter_init("h264_mp4toannexb");
#endif
while(av_read_frame(ifmt_ctx, &pkt)>=0){
if(pkt.stream_index==videoindex){
#if USE_H264BSF
av_bitstream_filter_filter(h264bsfc, ifmt_ctx->streams[videoindex]->codec, NULL, &pkt.data, &pkt.size, pkt.data, pkt.size, 0);
#endif
printf("Write Video Packet. size:%d\tpts:%lld\n",pkt.size,pkt.pts);
fwrite(pkt.data,1,pkt.size,fp_video);
}else if(pkt.stream_index==audioindex){
/*
AAC in some container format (FLV, MP4, MKV etc.) need to add 7 Bytes
ADTS Header in front of AVPacket data manually.
Other Audio Codec (MP3...) works well.
*/
printf("Write Audio Packet. size:%d\tpts:%lld\n",pkt.size,pkt.pts);
fwrite(pkt.data,1,pkt.size,fp_audio);
}
av_free_packet(&pkt);
}
#if USE_H264BSF
av_bitstream_filter_close(h264bsfc);
#endif
fclose(fp_video);
fclose(fp_audio);
avformat_close_input(&ifmt_ctx);
if (ret < 0 && ret != AVERROR_EOF) {
printf( "Error occurred.\n");
return -1;
}
return 0;
}
結果
輸入檔案為:cuc_ieschool.flv:FLV封裝格式資料。
輸出檔案為:
cuc_ieschool.h264:H.264視訊碼流資料。
cuc_ieschool.mp3:Mp3音訊碼流資料。
下載
simplest ffmpeg format
專案主頁
CSDN下載地址:工程中包含4個例子:
simplest_ffmpeg_demuxer_simple:視音訊分離器(簡化版)。
simplest_ffmpeg_demuxer:視音訊分離器。
simplest_ffmpeg_muxer:視音訊複用器。
simplest_ffmpeg_remuxer:封裝格式轉換器。
更新-1.1==================================================
修復了以下問題:
(1)Release版本下的執行問題
(2)simplest_ffmpeg_muxer封裝H.264裸流的時候丟失聲音的錯誤
CSDN下載
更新-1.2 (2015.2.13)=========================================
這次考慮到了跨平臺的要求,調整了原始碼。經過這次調整之後,原始碼可以在以下平臺編譯通過:
VC++:開啟sln檔案即可編譯,無需配置。
cl.exe:開啟compile_cl.bat即可命令列下使用cl.exe進行編譯,注意可能需要按照VC的安裝路徑調整腳本里面的引數。編譯命令如下。
::VS2010 Environment
call "D:\Program Files\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"
::include
@set INCLUDE=include;%INCLUDE%
::lib
@set LIB=lib;%LIB%
::compile and link
cl simplest_ffmpeg_demuxer_simple.cpp /link avcodec.lib avformat.lib avutil.lib ^
avdevice.lib avfilter.lib postproc.lib swresample.lib swscale.lib /OPT:NOREF
MinGW:MinGW命令列下執行compile_mingw.sh即可使用MinGW的g++進行編譯。編譯命令如下。
g++ simplest_ffmpeg_demuxer_simple.cpp -g -o simplest_ffmpeg_demuxer_simple.exe \
-I /usr/local/include -L /usr/local/lib -lavformat -lavcodec -lavutil
GCC:Linux或者MacOS命令列下執行compile_gcc.sh即可使用GCC進行編譯。編譯命令如下。
gcc simplest_ffmpeg_demuxer_simple.cpp -g -o simplest_ffmpeg_demuxer_simple.out \
-I /usr/local/include -L /usr/local/lib -lavformat -lavcodec -lavutil
PS:相關的編譯命令已經儲存到了工程資料夾中
CSDN下載地址:http://download.csdn.net/detail/leixiaohua1020/8445303
SourceForge上已經更新。
相關推薦
最簡單的基於FFmpeg的封裝格式處理:視音訊分離器簡化版(demuxer-simple)
=====================================================最簡單的基於FFmpeg的封裝格式處理系列文章列表:=====================================================簡介打算記錄
最簡單的基於FFmpeg的封裝格式處理 視音訊分離器簡化版(demuxer-simple)
=====================================================最簡單的基於FFmpeg的封裝格式處理系列文章列表:=====================================================簡介打算記錄一
基於FFmpeg和OpenSL ES的Android音訊播放器實現
前言 在以前的博文中,我們通過FFmpeg解碼,並基於OpenGL ES完成了視訊的渲染。本文我們將基於OpenSL ES完成native音訊的注入播放。 OpenSL ES也是The Khronos Group Inc組織制定的一個音訊規範,網上資料很多,
最簡單的基於FFmpeg的封裝格式處理 視音訊複用器 muxer
=====================================================最簡單的基於FFmpeg的封裝格式處理系列文章列表:=====================================================簡介打算記錄一
最簡單的基於FFmpeg的解碼器-純淨版(不包含libavformat)
=====================================================最簡單的基於FFmpeg的視訊播放器系列文章列表:=====================================================本文記錄一個更
iOS音視訊—FFmepg基礎知識:命令列工具使用&封裝格式&視訊編碼音訊編碼資料瞭解&視訊畫素音訊取樣資料格式
iOS音視訊相關目錄 FFmepg基礎知識 封裝格式 1、封裝格式:mp4、mov、flv、wmv等等… 2、作用:視訊流+音訊流按照格式進行儲存在一個檔案中 3、MPEG2-TS格式:傳輸流,又稱TS、TP、MPEG-TS或M2T,用於音效、影象與資料的通訊協議。屬於
MongoDB最簡單的入門教程之四:使用Spring Boot操作MongoDB
Spring Boot 是一個輕量級框架,可以完成基於 Spring 的應用程式的大部分配置工作。Spring Boot的目的是提供一組工具,以便快速構建容易配置的Spring應用程式,省去大量傳統Spring專案的繁瑣配置。 MongoDB是一個基於分散式檔
呼叫FFmpeg SDK解析封裝格式的視訊為音訊流和視訊流
我們平常最常用的音視訊檔案通常不是單獨的音訊訊號和視訊訊號,而是一個整體的檔案。這個檔案會在其中包含音訊流和視訊流,並通過某種方式進行同步播放。通常,檔案的音訊和視訊通過某種標準格式進行復用,生成某種封裝格式,而封裝的標誌就是檔案的副檔名,常用的有mp4/a
史上最簡單的 SpringCloud 教程 | 第一篇: 服務的註冊與發現(Eureka)
一、spring cloud簡介 spring cloud 為開發人員提供了快速構建分散式系統的一些工具,包括配置管理、服務發現、斷路器、路由、微代理
史上最簡單的 SpringCloud 教程 | 第一篇: 服務的註冊與發現Eureka(Finchley版本)
一、spring cloud簡介 鑑於《史上最簡單的Spring Cloud教程》很受讀者歡迎,再次我特意升級了一下版本,目前支援的版本為Spring Boot版本2.0.3.RELEASE,Spring Cloud版本為Finchley.RELEASE。
多媒體封裝格式學習:H264封裝成FLV(一)
搞了好幾天的FLV封裝,話說封裝真是個苦力活,有時候思路不是很清晰的時候,真心有點亂。 網上關於H264封裝成FLV的文件,都分析的很詳細了,但是有幾個點沒有考慮到,一會在下面我會一一跟大家說明。圖什麼的我就不畫了,網上一搜應該有很多,那
問題:git處理中文名稱時候顯示為編碼形式(已解決)
問題描述: Untracked files: (use "git add <file>..." to include in what will be committed) static/README.md "\350\207\252\346\2
簡單的佇列模版類處理執行緒間的資料同步問題(資料處理時間固定)
#include <iostream> #include <string> #include <vector> #include <math.h> #include <sstream> #include <thre
零基礎入門學習Python(33)--異常處理:你不可能總是對的(2)
前言 接下來我們介紹一種方法捕捉語句塊中可能出現的錯誤 知識點 異常處理 捕捉異常可以使用try/except語句。 try/except語句用來檢測try語句塊中的錯誤,從而讓except語句捕獲異常資訊並處理。 如果你不想在異常發生時結束你的程式,只需在try
基於ffmpeg重取樣、取樣精度轉換、通道數轉換(未測,需要修改部分變數)的例子
#include "stdafx.h" #include <stdio.h> #include <iostream> using namespace std; extern "C" { #include "libavformat/avformat.h" #include
Python資料預處理:機器學習、人工智慧通用技術(1)
Python資料預處理:機器學習、人工智慧通用技術 白寧超 2018年12月24日17:28:26 摘要:大資料技術與我們日常生活越來越緊密,要做大資料,首要解決資料問題。原始資料存在大量不完整、不一致、有異常的資料,嚴重影響到資料建模的執行效率,甚至可能導致模型
FFmpeg學習6:視音訊同步
轉載自:http://www.cnblogs.com/wangguchangqing/p/5900426.html 謝謝版主 在上一篇文章中,視訊和音訊是各自獨立播放的,並不同步。本文主要描述瞭如何以音訊的播放時長為基準,將視訊同步到音訊上以實現視音訊的同步播
最簡單的混合APP開發框架——搭建你的第一個Ionic應用(一)
上次寫了一篇關於Ionic3的文章,但是對於從來沒有接觸過Ionic的開發者來說,可能不是太友好。為了讓更多的人瞭解這個非常好的混合應用開發框架,今天這篇文章主要介紹如何從零用最快的時間做一個Ionic APP。 一)為什麼是Ionic? 如果你以前從來沒有
最簡單易懂的RxJava2.0學習教程之RxJava2的執行緒排程(二)
一、RxJava2執行緒排程 使用RxJava的時候,在沒有切換執行緒的情況下, 上游(observable)和下游(observer)是工作在同一個執行緒中的,即都在主執行緒中。 話不多說上程式碼: Observable<Integer>
史上最簡單的SpringCloud教程 | 第五篇: 路由閘道器(zuul)
在微服務架構中,需要幾個基礎的服務治理元件,包括服務註冊與發現、服務消費、負載均衡、斷路器、智慧路由、配置管理等,由這幾個基礎元件相互協作,共同組建了一個簡單的微服務系統。一個簡答的微服務系統如下圖: 注意:A服務和B服務是可以相互呼叫的,作圖的時候忘記了。並且配置