-
Notifications
You must be signed in to change notification settings - Fork 59
/
Copy pathh264decoder.cpp
175 lines (140 loc) · 4.1 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
175
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavutil/imgutils.h>
#include <libavutil/mem.h>
#include <libswscale/swscale.h>
}
#include "h264decoder.hpp"
typedef unsigned char ubyte;
/* For backward compatibility with release 9 or so of libav */
#if (LIBAVCODEC_VERSION_MAJOR <= 54)
# define av_frame_alloc avcodec_alloc_frame
# define av_frame_free avcodec_free_frame
#endif
H264Decoder::H264Decoder()
{
avcodec_register_all();
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");
// Note: CODEC_CAP_TRUNCATED was prefixed with AV_...
if(codec->capabilities & AV_CODEC_CAP_TRUNCATED) {
context->flags |= AV_CODEC_FLAG_TRUNCATED;
}
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");
frame = av_frame_alloc();
if (!frame)
throw H264InitFailure("cannot allocate frame");
#if 1
pkt = new AVPacket;
if (!pkt)
throw H264InitFailure("cannot allocate packet");
av_init_packet(pkt);
#endif
}
H264Decoder::~H264Decoder()
{
av_parser_close(parser);
avcodec_close(context);
av_free(context);
av_frame_free(&frame);
#if 1
delete pkt;
#endif
}
ptrdiff_t 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);
return nread;
}
bool H264Decoder::is_frame_available() const
{
return pkt->size > 0;
}
const AVFrame& H264Decoder::decode_frame()
{
#if (LIBAVCODEC_VERSION_MAJOR > 56)
int ret;
if (pkt) {
ret = avcodec_send_packet(context, pkt);
if (!ret) {
ret = avcodec_receive_frame(context, frame);
if (!ret)
return *frame;
}
}
throw H264DecodeFailure("error decoding frame");
#else
int got_picture = 0;
int nread = avcodec_decode_video2(context, frame, &got_picture, pkt);
if (nread < 0 || got_picture == 0)
throw H264DecodeFailure("error decoding frame\n");
return *frame;
#endif
}
ConverterRGB24::ConverterRGB24()
{
framergb = av_frame_alloc();
if (!framergb)
throw H264DecodeFailure("cannot allocate frame");
context = nullptr;
}
ConverterRGB24::~ConverterRGB24()
{
sws_freeContext(context);
av_frame_free(&framergb);
}
const AVFrame& ConverterRGB24::convert(const AVFrame &frame, ubyte* out_rgb)
{
int w = frame.width;
int h = frame.height;
int pix_fmt = frame.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 framergb with out_rgb as external buffer. Also say that we want RGB24 output.
av_image_fill_arrays(framergb->data, framergb->linesize, out_rgb, AV_PIX_FMT_RGB24, w, h, 1);
// Do the conversion.
sws_scale(context, frame.data, frame.linesize, 0, h,
framergb->data, framergb->linesize);
framergb->width = w;
framergb->height = h;
return *framergb;
}
/*
Determine required size of framebuffer.
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(framergb->data, framergb->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);
}