音视频编解码基础
编码


视频编码
- 初始化编码器并设置参数
- 初始化 AVPacket 和 AVFrame ,设置参数
- 读取视频文件,进行编码
- 释放内存,结束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
extern "C"
{
}
const char* inFileName = "input.yuv";
const char* outFileName = "output.h264";
int encode(AVCodecContext* codecContent, AVPacket* packet, AVFrame* frame, FILE* outFile)
{
//编码
int ret = avcodec_send_frame(codecContent, frame);
if (ret < 0)
{
qDebug() << "Error sending a frame for encoding";
return -1;
}
while (ret == 0)
{
ret = avcodec_receive_packet(codecContent, packet);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
return 0;
}
else if (ret < 0) {
qDebug() <<"Error encoding video frame";
return -1;
}
if (ret == 0)
{
fwrite(packet->data, 1, packet->size, outFile);
}
}
}
int main(int argc, char* argv[])
{
int ret = 0;
const AVCodec* codec = nullptr;
AVCodecContext* codecContent = nullptr;
AVPacket* packet = nullptr;
AVFrame* frame = nullptr;
FILE* inFile = nullptr;
FILE* outFile = nullptr;
//查找指定编码器
codec = avcodec_find_encoder(AV_CODEC_ID_H264);
if (codec == nullptr)
{
qDebug() <<"could not find h264 encoder!";
return -1;
}
//申请编码器上下文
codecContent = avcodec_alloc_context3(codec);
if (codecContent == nullptr)
{
qDebug() <<"could not alloc h264 content!";
return -1;
}
//必设参数
codecContent->width = 1280;
codecContent->height = 720;
codecContent->time_base = AVRational{ 1, 25 };
codecContent->pix_fmt = AV_PIX_FMT_YUV420P;
codecContent->framerate = AVRational{ 25, 1 };
//初始化编码器上下文
ret = avcodec_open2(codecContent, codec, NULL);
if (ret < 0) {
qDebug() << "Could not open codec";
exit(1);
}
packet = av_packet_alloc();
if (packet == nullptr)
{
qDebug() << "alloc packet error";
return -1;
}
frame = av_frame_alloc();
if (packet == nullptr)
{
qDebug() <<"alloc frame error";
return -1;
}
//必设参数
frame->width = codecContent->width;
frame->height = codecContent->height;
frame->format = codecContent->pix_fmt;
//申请视频数据存储空间
ret = av_frame_get_buffer(frame, 0);
if (ret)
{
qDebug() <<"alloc frame buffer error!";
return -1;
}
inFile = fopen(inFileName, "rb");
if (inFile == nullptr)
{
qDebug() <<"error to open file";
return -1;
}
outFile = fopen(outFileName, "wb");
if (inFile == nullptr)
{
qDebug() <<"error to open file";
return -1;
}
while (!feof(inFile))
{
ret = av_frame_is_writable(frame);
if (ret < 0)
{
ret = av_frame_make_writable(frame);
}
fread(frame->data[0], 1, frame->width * frame->height, inFile); //y
fread(frame->data[1], 1, frame->width * frame->height / 4, inFile); //u
fread(frame->data[2], 1, frame->width * frame->height / 4, inFile); //v
qDebug() << "encode frame";
encode(codecContent, packet, frame, outFile);
}
encode(codecContent, packet, nullptr, outFile);
qDebug() <<"encode finish";
av_packet_free(&packet);
av_frame_free(&frame);
avcodec_free_context(&codecContent);
fclose(inFile);
fclose(outFile);
return 0;
}
音频编码
- 初始化编码器并设置参数
- 创建输出上下文,写入 AAC 文件头,初始化音频重采样
- 初始化 AVPacket 和 AVFrame,设置参数
- 读取视频文件,进行重采样,在进行编码
- 写入文件尾,释放内存,结束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
extern "C"
{
}
int main(){
char inputfile[] = "C:\\Users\\edoYun\\Videos\\output.pcm";
char outputfile[] = "audio.aac";
const AVCodec* avCodec = avcodec_find_encoder(AV_CODEC_ID_AAC);
if(!avCodec){
qDebug() << "avcodec_find_encoder failed";
return -1;
}
AVCodecContext* avCodecContext = avcodec_alloc_context3(avCodec);
if(!avCodecContext){
qDebug() << "avcodec_alloc_context3 failed";
return -1;
}
int ret=0;
avCodecContext->sample_rate = 44100;
avCodecContext->channels = 2;
avCodecContext->sample_fmt = AV_SAMPLE_FMT_FLTP;
avCodecContext->bit_rate = 64000;
avCodecContext->channel_layout = AV_CH_LAYOUT_STEREO;
ret = avcodec_open2(avCodecContext, avCodec,NULL);
if(ret<0){
qDebug() << "avcodec_open2 failed";
return -1;
}
AVFormatContext* avFormatContext = NULL;
avformat_alloc_output_context2(&avFormatContext, NULL, NULL, outputfile);
if(!avFormatContext){
qDebug() << "avformat_alloc_output_context2 failed";
return -1;
}
AVStream* st = avformat_new_stream(avFormatContext, NULL);
st->codecpar->codec_tag = 0;
avcodec_parameters_from_context(st->codecpar, avCodecContext);
ret = avio_open(&avFormatContext->pb, outputfile, AVIO_FLAG_WRITE);
if(ret<0){
qDebug() << "avio_open failed";
return -1;
}
avformat_write_header(avFormatContext, NULL);
SwrContext* swrContext = NULL;
// 设置参数, 1. 上下文 2.输出声道布局 4.输出采样率, 5.输入声道布局 6.输入样本格式 7.输入采样率 8.配音 9.日志
swrContext = swr_alloc_set_opts(swrContext, avCodecContext->channel_layout, avCodecContext->sample_fmt, avCodecContext->sample_rate,
AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16, 44100,
0, 0);
if(!swrContext){
qDebug() << "swr_alloc_set_opts failed";
return -1;
}
// 初始化
ret = swr_init(swrContext);
if(ret<0){
qDebug() << "swr_init failed";
return -1;
}
AVFrame* avFrame = av_frame_alloc();
avFrame->format = AV_SAMPLE_FMT_FLTP;
avFrame->channels=2;
avFrame->channel_layout = AV_CH_LAYOUT_STEREO;
avFrame->nb_samples=1024;
ret = av_frame_get_buffer(avFrame, 0);
if(ret<0){
qDebug() << "av_frame_get_buffer failed";
return -1;
}
int readSize = avFrame->nb_samples*2*2; //双声道,float 16位
char* pcms = new char[readSize];
FILE* fp = fopen(inputfile, "rb");
for(;;){
AVPacket pkt;
av_init_packet(&pkt);
int len = fread(pcms,1,readSize, fp);
if(len <=0){
break;
}else{
const uint8_t* data[1];
data[0] = (uint8_t*)pcms;
len = swr_convert(swrContext, avFrame->data, avFrame->nb_samples, data, avFrame->nb_samples);
if(len <=0){
qDebug() << "swr_convert failed";
break;
}
ret = avcodec_send_frame(avCodecContext, avFrame);
if(ret < 0){
qDebug() << "avcodec_send_frame failed";
continue;
}
ret = avcodec_receive_packet(avCodecContext, &pkt);
if(ret == 0){
av_interleaved_write_frame(avFormatContext, &pkt);
}
}
}
av_write_trailer(avFormatContext);
fclose(fp);
avio_close(avFormatContext->pb);
avcodec_close(avCodecContext);
avcodec_free_context(&avCodecContext);
avformat_free_context(avFormatContext);
return 0;
}
解码
音频解码

注:在 FFmpeg 中有两种表示 PCM 数据包的模式,分别是 planar 和 packed 模式。
其中 packed 又叫交错模式,而 planar 又叫平面模式,所谓交错或平面就是不同声道的声音信号排列储存的方式,例如对于一个双声道的 PCM 数据来说,
1 | 用packed模式表示是这样子的: |
1 | 在FFmpeg中,packed模式的格式有: |
1 |
|
视频解码

1 |
|
其他
6
AVFrame 是未压缩的图像或音频样本,AVPacket 是编码后的数据包。
PTS 是应该展示的时间,DTS 是解码所需的时间;有帧重排时二者可能不同。time_base 是一个时间刻度的长度:time_base=1/30 时 PTS=60 表示 2 秒;音频若以 1/48000 为刻度,PTS=96000 也表示 2 秒。必须换到同一单位再比较。
GOP 是一段有参考关系的画面序列。关键帧提供恢复解码的重要入口,常见 P 帧会依赖参考帧。IDR 还承担刷新参考关系的作用,不能把所有“标记为关键”的帧都不加区别地当作万能恢复点。
https://blog.csdn.net/wkd_007/article/details/134966687
https://www.cnblogs.com/samirchen/p/7071824.html
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 逸人の博客!
评论

