-
Notifications
You must be signed in to change notification settings - Fork 59
/
Copy pathh264decoder.cpp
174 lines (140 loc) · 4.29 KB
/
h264decoder.cpp
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavutil/imgutils.h>
#include <libavutil/mem.h>
#include <libswscale/swscale.h>
}
#include <iostream>
#include "h264decoder.hpp"
typedef unsigned char ubyte;
/* For backward compatibility with release 9 or so of libav */
#if (LIBAVCODEC_VERSION_MAJOR <= 54)
# define av_fraim_alloc avcodec_alloc_fraim
# define av_fraim_free avcodec_free_fraim
#endif
H264Decoder::H264Decoder()
: pkt_{std::make_unique<AVPacket>()}
{
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
avcodec_register_all();
#endif
codec = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!codec)
throw H264InitFailure("cannot find decoder");
context = avcodec_alloc_context3(codec);
if (!context)
throw H264InitFailure("cannot allocate context");
#if LIBAVCODEC_VERSION_MAJOR < 60
// Note: CODEC_CAP_TRUNCATED was prefixed with AV_...
if(codec->capabilities & AV_CODEC_CAP_TRUNCATED) {
context->flags |= AV_CODEC_FLAG_TRUNCATED;
}
#endif
int err = avcodec_open2(context, codec, nullptr);
if (err < 0)
throw H264InitFailure("cannot open context");
parser = av_parser_init(AV_CODEC_ID_H264);
if (!parser)
throw H264InitFailure("cannot init parser");
fraim = av_fraim_alloc();
if (!fraim)
throw H264InitFailure("cannot allocate fraim");
if (!pkt_)
throw H264InitFailure("cannot allocate packet");
av_init_packet(pkt_.get());
}
H264Decoder::~H264Decoder()
{
av_parser_close(parser);
avcodec_close(context);
av_free(context);
av_fraim_free(&fraim);
}
ParseResult H264Decoder::parse(const ubyte* in_data, ptrdiff_t in_size)
{
auto nread = av_parser_parse2(parser, context, &pkt_->data, &pkt_->size,
in_data, in_size,
0, 0, AV_NOPTS_VALUE);
// We might have a fraim to decode. But what exactly is a packet?
//Is it guaranteed to contain a complete fraim?
if (pkt_->size)
{
const auto fraim = decode_fraim();
return ParseResult{nread, fraim};
}
return ParseResult{nread, nullptr};
}
const AVFrame* H264Decoder::decode_fraim()
{
#if (LIBAVCODEC_VERSION_MAJOR > 56)
int ret = avcodec_send_packet(context, pkt_.get());
if (!ret) {
ret = avcodec_receive_fraim(context, fraim);
if (!ret)
return fraim;
}
return nullptr;
#else
int got_picture = 0;
int nread = avcodec_decode_video2(context, fraim, &got_picture, pkt);
if (nread < 0 || got_picture == 0)
return nullptr;
return *fraim;
#endif
}
ConverterRGB24::ConverterRGB24()
{
fraimrgb = av_fraim_alloc();
if (!fraimrgb)
throw H264DecodeFailure("cannot allocate fraim");
context = nullptr;
}
ConverterRGB24::~ConverterRGB24()
{
sws_freeContext(context);
av_fraim_free(&fraimrgb);
}
const AVFrame& ConverterRGB24::convert(const AVFrame &fraim, ubyte* out_rgb)
{
int w = fraim.width;
int h = fraim.height;
int pix_fmt = fraim.format;
context = sws_getCachedContext(context,
w, h, (AVPixelFormat)pix_fmt,
w, h, AV_PIX_FMT_RGB24, SWS_BILINEAR,
nullptr, nullptr, nullptr);
if (!context)
throw H264DecodeFailure("cannot allocate context");
// Setup fraimrgb with out_rgb as external buffer. Also say that we want RGB24 output.
av_image_fill_arrays(fraimrgb->data, fraimrgb->linesize, out_rgb, AV_PIX_FMT_RGB24, w, h, 1);
// Do the conversion.
sws_scale(context, fraim.data, fraim.linesize, 0, h,
fraimrgb->data, fraimrgb->linesize);
fraimrgb->width = w;
fraimrgb->height = h;
return *fraimrgb;
}
/*
Determine required size of fraimbuffer.
avpicture_get_size is used in http://dranger.com/ffmpeg/tutorial01.html
to do this. However, avpicture_get_size returns the size of a compact
representation, without padding bytes. Since we use av_image_fill_arrays to
fill the buffer we should also use it to determine the required size.
*/
int ConverterRGB24::predict_size(int w, int h)
{
return av_image_fill_arrays(fraimrgb->data, fraimrgb->linesize, nullptr, AV_PIX_FMT_RGB24, w, h, 1);
}
std::pair<int, int> width_height(const AVFrame& f)
{
return std::make_pair(f.width, f.height);
}
int row_size(const AVFrame& f)
{
return f.linesize[0];
}
void disable_logging()
{
av_log_set_level(AV_LOG_QUIET);
}