1. 程式人生 > >最簡單的基於FFMPEG的封裝格式轉換器(無編解碼)

最簡單的基於FFMPEG的封裝格式轉換器(無編解碼)

=====================================================

最簡單的基於FFmpeg的封裝格式處理系列文章列表:

=====================================================

簡介

本文介紹一個基於FFMPEG的封裝格式轉換器。所謂的封裝格式轉換,就是在AVI,FLV,MKV,MP4這些格式之間轉換(對應.avi,.flv,.mkv,.mp4檔案)。需要注意的是,本程式並不進行視音訊的編碼和解碼工作。而是直接將視音訊壓縮碼流從一種封裝格式檔案中獲取出來然後打包成另外一種封裝格式的檔案。傳統的轉碼程式工作原理如下圖所示:


上圖例舉了一個舉例:FLV(視訊:H.264,音訊:AAC)轉碼為AVI(視訊:MPEG2,音訊MP3)的例子。可見視訊轉碼的過程通俗地講相當於把視訊和音訊重新“錄”了一遍。
本程式的工作原理如下圖所示:

由圖可見,本程式並不進行視訊和音訊的編解碼工作,因此本程式和普通的轉碼軟體相比,有以下兩個特點:
處理速度極快。視音訊編解碼演算法十分複雜,佔據了轉碼的絕大部分時間。因為不需要進行視音訊的編碼和解碼,所以節約了大量的時間。

視音訊質量無損。因為不需要進行視音訊的編碼和解碼,所以不會有視音訊的壓縮損傷。

流程(2014.9.29更新)

下面附上基於FFmpeg的Remuxer的流程圖。圖中使用淺紅色標出了關鍵的資料結構,淺藍色標出了輸出視訊資料的函式。可見成個程式包含了對兩個檔案的處理:讀取輸入檔案(位於左邊)和寫入輸出檔案(位於右邊)。中間使用了一個avcodec_copy_context()拷貝輸入的AVCodecContext到輸出的AVCodecContext。


簡單介紹一下流程中關鍵函式的意義:

輸入檔案操作:

avformat_open_input():開啟輸入檔案,初始化輸入視訊碼流的AVFormatContext。

av_read_frame():從輸入檔案中讀取一個AVPacket。

輸出檔案操作:

avformat_alloc_output_context2():初始化輸出視訊碼流的AVFormatContext。

avformat_new_stream():建立輸出碼流的AVStream。

avcodec_copy_context():拷貝輸入視訊碼流的AVCodecContex的數值t到輸出視訊的AVCodecContext。

avio_open():開啟輸出檔案。

avformat_write_header():寫檔案頭(對於某些沒有檔案頭的封裝格式,不需要此函式。比如說MPEG2TS)。

av_interleaved_write_frame():將AVPacket(儲存視訊壓縮碼流資料)寫入檔案。

av_write_trailer():寫檔案尾(對於某些沒有檔案頭的封裝格式,不需要此函式。比如說MPEG2TS)。


程式碼

貼上程式碼,程式碼是從FFmpeg的例子改編的,平臺是VC2010。

/*
 *最簡單的基於FFmpeg的封裝格式轉換器
 *Simplest FFmpeg Remuxer
 *
 *雷霄驊 Lei Xiaohua
 *[email protected]
 *中國傳媒大學/數字電視技術
 *Communication University of China / Digital TV Technology
 *http://blog.csdn.net/leixiaohua1020
 *
 *本程式實現了視訊封裝格式之間的轉換。
 *需要注意的是本程式並不改變視音訊的編碼格式。
 *
 * This software converts a media file from one container format
 * to another container format without encoding/decoding video files.
 */
 
#include "stdafx.h"
 
extern "C"
{
#include "libavformat/avformat.h"
};
 
 
int _tmain(int argc, _TCHAR* argv[])
{
    AVOutputFormat *ofmt = NULL;
    //輸入對應一個AVFormatContext,輸出對應一個AVFormatContext
    //(Input AVFormatContext and Output AVFormatContext)
    AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
    AVPacket pkt;
    const char *in_filename, *out_filename;
    int ret, i;
    if (argc < 3) {
        printf("usage: %s input output\n"
            "Remux a media file with libavformat and libavcodec.\n"
            "The output format is guessed according to the file extension.\n"
            "Modified by Lei Xiaohua, [email protected]\n"
            "Communication University of China / Digital TV Technology\n"
            "http://blog.csdn.net/leixiaohua1020", argv[0]);
        return 1;
    }
    in_filename  = argv[1];//輸入檔名(Input file URL)
    out_filename = argv[2];//輸出檔名(Output file URL)
    av_register_all();
    //輸入(Input)
    if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
        printf( "Could not open input file.");
        goto end;
    }
    if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
        printf( "Failed to retrieve input stream information");
        goto end;
    }
    av_dump_format(ifmt_ctx, 0, in_filename, 0);
    //輸出(Output)
    avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
    if (!ofmt_ctx) {
        printf( "Could not create output context\n");
        ret = AVERROR_UNKNOWN;
        goto end;
    }
    ofmt = ofmt_ctx->oformat;
    for (i = 0; i < ifmt_ctx->nb_streams; i++) {
        //根據輸入流建立輸出流(Create output AVStream according to input AVStream)
        AVStream *in_stream = ifmt_ctx->streams[i];
        AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);
        if (!out_stream) {
            printf( "Failed allocating output stream\n");
            ret = AVERROR_UNKNOWN;
            goto end;
        }
        //複製AVCodecContext的設定(Copy the settings of AVCodecContext)
        ret = avcodec_copy_context(out_stream->codec, in_stream->codec);
        if (ret < 0) {
            printf( "Failed to copy context from input to output stream codec context\n");
            goto end;
        }
        out_stream->codec->codec_tag = 0;
        if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
            out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
    }
    //輸出一下格式------------------
    av_dump_format(ofmt_ctx, 0, out_filename, 1);
    //開啟輸出檔案(Open output file)
    if (!(ofmt->flags & AVFMT_NOFILE)) {
        ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
        if (ret < 0) {
            printf( "Could not open output file '%s'", out_filename);
            goto end;
        }
    }
    //寫檔案頭(Write file header)
    ret = avformat_write_header(ofmt_ctx, NULL);
    if (ret < 0) {
        printf( "Error occurred when opening output file\n");
        goto end;
    }
    int frame_index=0;
    while (1) {
        AVStream *in_stream, *out_stream;
        //獲取一個AVPacket(Get an AVPacket)
        ret = av_read_frame(ifmt_ctx, &pkt);
        if (ret < 0)
            break;
        in_stream  = ifmt_ctx->streams[pkt.stream_index];
        out_stream = ofmt_ctx->streams[pkt.stream_index];
        /* copy packet */
        //轉換PTS/DTS(Convert PTS/DTS)
        pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
        pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
        pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
        pkt.pos = -1;
        //寫入(Write)
        ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
        if (ret < 0) {
            printf( "Error muxing packet\n");
            break;
        }
        printf("Write %8d frames to output file\n",frame_index);
        av_free_packet(&pkt);
        frame_index++;
    }
    //寫檔案尾(Write file trailer)
    av_write_trailer(ofmt_ctx);
end:
    avformat_close_input(&ifmt_ctx);
    /* close output */
    if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
        avio_close(ofmt_ctx->pb);
    avformat_free_context(ofmt_ctx);
    if (ret < 0 && ret != AVERROR_EOF) {
        printf( "Error occurred.\n");
        return -1;
    }
    return 0;
}

除錯的時候,只需要“右鍵工程->除錯->命令列引數”裡面設定輸入的檔名和輸出的檔名就可以了。

結果

下圖顯示了一個測試的輸入檔案的視音訊引數。

 下圖顯示了輸出檔案的視音訊引數。可以看出除了視訊的封裝格式從flv轉換成了mp4,其他有關視音訊編碼的引數沒有任何變化。


下載

simplest ffmpeg format

專案主頁


CSDN下載:
http://download.csdn.net/detail/leixiaohua1020/8005317

工程中包含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_remuxer.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_remuxer.cpp -g -o simplest_ffmpeg_remuxer.exe \
-I /usr/local/include -L /usr/local/lib -lavformat -lavcodec -lavutil

GCC:Linux或者MacOS命令列下執行compile_gcc.sh即可使用GCC進行編譯。編譯命令如下。

gcc simplest_ffmpeg_remuxer.cpp -g -o simplest_ffmpeg_remuxer.out -I /usr/local/include -L /usr/local/lib \
-lavformat -lavcodec -lavutil

PS:相關的編譯命令已經儲存到了工程資料夾中

SourceForge上已經更新。

相關推薦

簡單基於FFMPEG封裝格式轉換解碼

=====================================================最簡單的基於FFmpeg的封裝格式處理系列文章列表:=====================================================簡介本文介紹

簡單封裝格式轉換(使用最新API)

編譯執行環境: System: MacOs IDE : Clion FFmpeg Version : 3.1.4 CMake : cmake_minimum_required(VERSION 3.6) project(FFmpegRemuxer)

簡單基於FFMPEG的視訊編碼YUV編碼為H.264

                =====================================================最簡單的基於FFmpeg的視訊編碼器文章列表:=====================================================本文介紹一個最簡單的

簡單基於FFMPEG的音訊編碼PCM編碼為AAC

                本文介紹一個最簡單的基於FFMPEG的音訊編碼器。該編碼器實現了PCM音訊取樣資料編碼為AAC的壓縮編碼資料。編碼器程式碼十分簡單,但是每一行程式碼都很重要。通過看本編碼器的原始碼,可以瞭解FFMPEG音訊編碼的流程。本程式使用最新版的類庫(編譯時間為2014.5.6),開發平

簡單基於FFMPEG的影象編碼YUV編碼為JPEG

伴隨著畢業論文的完成,這兩天終於騰出了空閒,又有時間搞搞FFMPEG的研究了。想著之前一直搞的都是FFMPEG解碼方面的工作,很少涉及到FFMPEG編碼方面的東西,於是打算研究一下FFMPEG的編碼。在網上看了一些例子,發現要不然是難度略微有些大,要不然就是類庫比較陳舊,於是

簡單基於FFmpeg的推流以推送RTMP為例

由於工作一部分工作是作為流媒體伺服器的程式設計師。所以自己那塊也算是處理了推流器的一塊程式碼吧。 這邊是從網上轉載的文章,原文:http://blog.csdn.net/leixiaohua1020/article/details/46890487 =========

費下載新版萬能視頻格式轉換是一款功能強大的全能視頻格式轉換軟件

轉換器 功能 down line 批量 簡單 href 設備 iphone 萬能視頻格式轉換器是一款功能強大的全能視頻格式轉換軟件,支持多種視頻格式轉換。萬能視頻轉換器可以將RM、RMVB、AVI、WMV、MPG 、MPEG、FLV、3GP、MP4、SWF、ASF、DIVX

實現一個簡單的marked編輯格式轉換部分功能

首先需要在專案裡安裝marked格式編輯包 在專案根目錄下執行npm install marked 安裝依賴包 至此,package.json裡面 dependencies 已經新增     "marked": "^0.5.1", 然後在需要顯示的元件裡編寫顯示區域

簡單基於libVLC的例子:簡單基於libVLC的視訊播放圖形介面版

=====================================================最簡單的基於libVLC的例子文章列表:=====================================================本文記錄使用libVLC

簡單基於FFmpeg的libswscale的示例YUV轉RGB

=====================================================最簡單的基於FFmpeg的libswscale的示例系列文章列表:====================================================

SpringMVC寫一個時間格式轉換DateConverter

在工具包裡寫一個時間格式轉換類: package com.neuedu.crm.utils; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.D

基於Darknet框架訓練分類cifar10資料集+windows

參考  https://pjreddie.com/darknet/train-cifar/ 1 下載資料集 https://pjreddie.com/media/files/cifar.tgz  在該網址下下載cifar資料集,並解壓在darknet.exe

python資料探勘入門與實踐--------轉換資料與處理與流水線

y=MinMaxScaler().fit_transform(x)  y與x為同型矩陣,y每列值的值域為0到1 sklearn.preprocessing.Normalizer 每條資料各特徵值的和為1 sklearn.preprocessing.StandardScaler 各特

簡單基於FFmpeg的AVDevice樣例讀取攝像頭

malloc projects == 格式 mac 跨平臺 buffer 版本 span =====================================================最簡單的基於FFmpeg的AVDevice樣例文章列表:最簡單的基於FFmp

簡單基於FFmpeg的AVfilter樣例水印疊加

中國 exiting endpoint write mod 無需 它的 fopen cap =====================================================最簡單的基於FFmpeg的AVfilter樣例系列文章:最簡單的基於FF

音頻格式轉換哪個快最好用

shadow ecb 不錯 proc 功能 中大 col 文件操作 它的 在我們的日常工作中,許多人開始嘗試著將音頻進行格式轉換,它簡單卻很有效,利用音頻轉換工具,將不同的格式進行轉換,讓我們隨意轉換自己喜歡的格式,那麽,哪款轉換器簡單又好用呢?如何簡單快速地進行音頻格式轉

分享基於.NET動態編譯&Newtonsoft.Json封裝實現JSON轉換JsonConverter原理及JSON操作技巧

  看文章標題就知道,本文的主題就是關於JSON,JSON轉換器(JsonConverter)具有將C#定義的類原始碼直接轉換成對應的JSON字串,以及將JSON字串轉換成對應的C#定義的類原始碼,而JSON操作技巧則說明如何通過JPath來快速的定位JSON的屬性節點從而達到靈活讀寫JSON目的。 一、J

哪款DWG轉PDF格式轉換比較不錯

  DWG是我我們最常使用的一中CAD文件格式,有時候我們在編輯完DWG之後,在一些特殊情況下我們將DWG轉換成更安全、更容易查看的PDF文件格式。那麽如果我們想要將DWG文件轉換成PDF格式我們怎麽轉換?DWG轉PDF格式轉換器應該用哪一款? 想要將DWG轉換成PDF格式,就需要使用到轉換的文件轉換器

PDF文件轉換DWG格式轉換

PDF文件用處很多,也得到了廣泛的應用,不可二次編輯即是PDF文件的優點也是PDF文件的缺點,優點是避免PDF文件在傳看中更改或者抄襲,缺點不可二次編輯,原有的PDF文件想要更改卻改不小,那我們怎麽去改變PDF文件這一缺點呢? PDF文件不可編輯,我們需要將PDF文件轉換DWG文件格式,那麽怎麽轉換呢?

基於 ffmpeg 的跨平臺播放實現

空間 編解碼 流程 position eat clu ict 網絡協議 紋理貼圖 https://www.qcloud.com/community/article/309889001486708756 背景: 隨著遊戲娛樂等直播業務的增長,在移動端觀看直播的需求也日益迫切。