转封装是指在mp4、flv、avi等文件格式之间的转换。
常用见视频封装格式(容器):
容器 AVI(Audio Video Interleaved)即音视频交错格式.
AVI 符合 RIFF(Resource Interchange File Format)文件规范,使用四字符码 FOURCC(four-character code)来表征数据类型。AVI 的文件结构分为头部、主体和索引三部分。 主体中图像数据和声音数据是交互存放的,从尾部的索引可以索引跳到自己想放的位置。
AVI 本身只是提供了这么一个框架,内部的图像数据和声音数据格式可以是任意的编码形式。因为索引放在了文件尾部,所以在播网络流媒体时已属力不从心。一个很简单的例子,从网络上下载 AVI 文件,如果没有下载完成,是很难正常播放出来。
Chunks
typedef struct {
DWORD dwFourCC
DWORD dwSize //data
BYTE data[dwSize] // contains headers or video/audio data
} CHUNK;
Lists
typedef struct {
DWORD dwList
DWORD dwSize //dwFourcc + data
DWORD dwFourCC
BYTE data[dwSize-4] // contains Lists and Chunks
} LIST;
如上可知,Chunks 数据块由一个四字符码、4 字节 data size(指下面的数据大小)以及数据组成
List 由四部分组成,四个字节四字符码(“list”)、4 字节数据大小(指后面列的两部分数据大小)、四字节 list 类型以及数据组成,与 Chunk 数据块不同的是,List 数据内容可以包含字块(Chunk 或 List)。
因为HDDVD以及BD之争,尽管两家在编码上都统一采用MPEG2/VC-1/H.264,可在封装格式上又有所分岐。DVD论坛官方所认可的HDDVD使用的是PS封装,即Program Stream(程序流),这和之前DVD所采用的MPEG2 Program Stream封装是一样的,PS流的后缀名是VOB以及EVO等。而BD在没有DVD论坛官方认证的情况下,自然不是PS封装,而是使用了MPEG2的另一封装TS封装,即Transport Stream(传输流),TS流的后缀名为TS。它们都是MPEG2系统部分的两个不同的语法结构,而在现在仅仅在作为封装使用。TS流对于PS流来说更易传输,不过由于其性质,也更易出错,所以在以前一般存储方面都是使用PS流,当然现在随着容错/纠错技术的提高,TS的适用范围越来越广。
现在网上大多流传以TS封装的HDTV remux版,PS封装只能在HDDVD原版才看到,所以我们来着重分析一下TS封装格式。
电视节目是你任何时候打开电视机都能解码(收看)的,所以,MPEG2-TS格式的特点就是要求从视频流的任一片段开始都是可以独立解码的。从结构上来说,TS是由头文件和主体所组成的,扩充过的TS流还包括时间戳。这样不管是什么格式的VBR音轨,都很容易通过时间戳来同步图像。当然,对新的声音格式来说,需要新的分离器,解码器来实现解码。目前在不断改进开发中。
TS不像AVI,从诞生那天起,就考虑到了网络播放,所以很快成为了世界标准并广泛应用于电视台数字播放,手机等各个领域。
REMUX版本:
Remux的意思是无损的提取出HD-DVD和BluRay-DVD里面的视频数据和音频数据,封装到我们所熟悉的TC或AVI文件中。
基本流 (Elementary Streams)是直接从编码器出来的数据流,也成为净荷数据。ES是编码后的视频流(比如H.264),音频数据流(如AAC),和其他编码数据流的统称。
ES是只包含一种内容的数据流(比如纯粹的视频或音频),每个ES都由若干个存取单元(AU)组成,每个视频AU或音频AU都是由头部和编码数据两部分组成,1个AU相当于编码的1幅视频图像或1个音频帧,也可以说,每个AU实际上是编码数据流的显示单元,即相当于解码的1幅视频图像或1个音频帧的取样。
打包的ES(Packetized Elementary Streams),是用来传递ES的一种数据结构。是ES流经过 PES打包形成的数据流,即将ES流分组、打包、加入包头信息,是对ES流的第一次打包。
PTS - 显示时间戳(Presentation Time Stamp),用来表示显示单元出现在系统目标解码器的时间。
DTS - 解码时间戳(Decoding Time Stamp),用来表示将存取单元全部字节从解码缓存取走的时间。
PTS/DTS 这两个参数是解决音视频同步显示,防止解码器输入缓存上溢或下溢的关键。每一个 I帧 | P帧 | B帧 的包头都有一个PTS和DTS。
MOV是Quicktime封装,与AVI相近年代,也有很多缺陷,现在很少有人使用。
HDRIP: 重新编码,即有损压缩过之后的视频。
MKV是Matroska的简称,比较常见的搭配是X264+MKV。MKV封装十分新颖,而且也非常开放。:
显示时间戳(Presentation Time Stamp),用来表示显示单元出现在系统目标解码器的时间。
解码时间戳(Decoding Time Stamp),用来表示将存取单元全部字节从解码缓存取走的时间。
PTS/DTS 这两个参数是解决音视频同步显示,防止解码器输入缓存上溢或下溢的关键。每一个 I帧 | P帧 | B帧 的包头都有一个PTS和DTS。
QT += core
QT -= gui
TARGET = remuxing
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp
INCLUDEPATH +="D:\\tools\\ffmpeg\\win32\\dev\\include"
LIBS += -LD:\tools\ffmpeg\win32\dev\lib -lavutil -lavformat -lavcodec -lavdevice -lavfilter -lpostproc -lswresample -lswscale
#include
#include
#define __STDC_CONSTANT_MACORS
extern "C"
{
#include "libavformat/avformat.h"
}
int close();
AVOutputFormat *ofmt = NULL;
//输入输出各对应一个AVFormatContext
AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
int ret;
int main(int argc,char* argv[]){
QCoreApplication a(argc,argv);
AVPacket pkt;
const char *in_filename, *out_filename;
int i;
in_filename = "D:/1.flv"; //输入文件名(Input file URL)
out_filename = "D:/2.flv"; //输出文件名(Output file URL)
qDebug() << "BEGIN" << endl;
av_register_all();
// 打开输入文件
if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
qDebug() << "Could not open input file." << endl;
return close();
}
// 获取媒体信息
if((ret = avformat_find_stream_info(ifmt_ctx,0))<0){
qDebug() << "Failed to retrieve input stream information" << endl;
return close();
}
// 打印输入格式的详细信息,比如持续时间duration,比特率 bitrate,流 streams,容器 container, 程序programs,元数据 metadata, side data, 编解码 器codec ,时基 time base.
// 第2个参数,流索引
// 第3个参数,打印的url,如源文件或目标文件
// 第4个参数,is_output 选择指定的context是0输入 还是1输出
av_dump_format(ifmt_ctx, 0, in_filename, 0);
// 分配一个用于输出格式的AVFormatContext
avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
if(!ofmt_ctx){
qDebug() << "Could not create output context\n" << endl;
ret = AVERROR_UNKNOWN;
return close();
}
qDebug() << "hello" << endl;
ofmt = ofmt_ctx->oformat;
for(i=0;inb_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;
return close();
}
// 拷贝源AVCodecContext的设置到目的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");
return close();
}
out_stream->codec->codec_tag = 0;
if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
out_stream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; //CODEC_FLAG_GLOBAL_HEADER找不到,使用AV_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);
return close();
}
}
//写文件头(Write file header)
ret = avformat_write_header(ofmt_ctx, NULL);
if (ret < 0) {
printf( "Error occurred when opening output file\n");
return close();
}
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);
return a.exec();
}
// 释放资源
int close(){
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;
}
参考文章:
https://blog.csdn.net/qq_19923217/article/details/93774888