1. 程式人生 > >最簡單的基於FFmpeg的封裝格式處理:視音訊分離器簡化版(demuxer-simple)

最簡單的基於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

零基礎入門學習Python33--異常處理你不可能總是對的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服務是可以相互呼叫的,作圖的時候忘記了。並且配置