Skip to content

Commit ac9d248

Browse files
committed
MediaDecoder.*
1 parent 484f0bf commit ac9d248

File tree

2 files changed

+483
-0
lines changed

2 files changed

+483
-0
lines changed

MediaDecoder.cpp

+377
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,377 @@
1+
#include "MediaDecoder.hpp"
2+
3+
void
4+
MediaDecoder::initFfmpeg()
5+
{
6+
//av_log_set_callback(NULL);
7+
avcodec_register_all();
8+
av_register_all();
9+
}
10+
11+
void
12+
MediaDecoder::outputMessage(int level, const char* format, ...)
13+
{
14+
va_list vl;
15+
va_start(vl, format);
16+
17+
vprintf(format, vl);
18+
printf("\n");
19+
20+
va_end(vl);
21+
}
22+
23+
MediaDecoder::MediaDecoder()
24+
: mFormatContext(NULL),
25+
mSwsContext(NULL),
26+
mFirstVideoStreamIndex(-1),
27+
mScaleWidth(-1), mScaleHeight(-1), mScaleStride(-1), mScalePixelFormat(PIX_FMT_NONE)
28+
{
29+
mFrame = avcodec_alloc_frame();
30+
}
31+
32+
MediaDecoder::~MediaDecoder()
33+
{
34+
if (mSwsContext) {
35+
sws_freeContext(mSwsContext);
36+
mSwsContext = NULL;
37+
}
38+
if (mFrame) {
39+
av_free(mFrame);
40+
mFrame = NULL;
41+
}
42+
if (mFormatContext) {
43+
av_close_input_file(mFormatContext);
44+
mFormatContext = NULL;
45+
}
46+
}
47+
48+
49+
int
50+
MediaDecoder::openFile(const char* filename)
51+
{
52+
int intRet;
53+
54+
// open the input file.
55+
if (0 > (intRet = av_open_input_file(&mFormatContext, filename, NULL, 0, NULL))) {
56+
this->outputMessage(0, "Could not open input file: ret=%d file=%s", intRet, filename);
57+
return intRet;
58+
}
59+
60+
if (0 > (intRet = av_find_stream_info(mFormatContext))) {
61+
this->outputMessage(0, "av_find_stream_info() failed: ret=%d", intRet);
62+
return intRet;
63+
}
64+
65+
int streamCount = mFormatContext->nb_streams;
66+
67+
// prepare the streams.
68+
for (int i = 0; i < streamCount; ++i) {
69+
if (0 > (intRet = this->prepareStream(i))) {
70+
this->outputMessage(0, "prepareStream() failed: ret=%d", intRet);
71+
return intRet;
72+
}
73+
if (mFirstVideoStreamIndex == -1 && mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
74+
mFirstVideoStreamIndex = i;
75+
}
76+
}
77+
78+
if (mFirstVideoStreamIndex != -1) {
79+
AVCodecContext* codecContext = mFormatContext->streams[mFirstVideoStreamIndex]->codec;
80+
this->setScaleParameters(codecContext->width, codecContext->height, PIX_FMT_RGB32, Stride_4bytes);
81+
}
82+
83+
return 0;
84+
}
85+
86+
static int
87+
getSmallestPowerOfTwo(int n) {
88+
int f = 0;
89+
int c = 0;
90+
while (n > 1) {
91+
f |= (n & 1);
92+
n >>= 1;
93+
c++;
94+
}
95+
if (f) { c++; }
96+
return n << c;
97+
}
98+
99+
int
100+
MediaDecoder::setScaleParameters(int width, int height, PixelFormat pixelFormat, StrideMode strideMode)
101+
{
102+
AVCodecContext* codecContext = this->getVideoCodec();
103+
if (codecContext == NULL) {
104+
this->outputMessage(0, "No video stream found. Cannot set scale parameters.");
105+
return -1;
106+
}
107+
108+
mSwsContext = sws_getCachedContext(
109+
mSwsContext,
110+
codecContext->width,
111+
codecContext->height,
112+
codecContext->pix_fmt,
113+
width,
114+
height,
115+
pixelFormat,
116+
// SWS_LANCZOS,
117+
// SWS_BILINEAR,
118+
SWS_FAST_BILINEAR,
119+
NULL, NULL, NULL);
120+
if (!mSwsContext) {
121+
this->outputMessage(0, "sws_getContext failed.");
122+
return -1;
123+
}
124+
125+
// calc stride
126+
int tightLineSize = avpicture_get_size(pixelFormat, width, 1);
127+
int stride = -1;
128+
switch (strideMode) {
129+
case Stride_Tight:
130+
stride = tightLineSize;
131+
break;
132+
case Stride_PowerOfTwo:
133+
stride = getSmallestPowerOfTwo(tightLineSize);
134+
break;
135+
case Stride_4bytes:
136+
stride = ((tightLineSize + 3) >> 2) << 2;
137+
break;
138+
default:
139+
this->outputMessage(0, "Unknown stride mode: %d", strideMode);
140+
return -1;
141+
}
142+
143+
mScaleStride = stride;
144+
mScaleWidth = width;
145+
mScaleHeight = height;
146+
mScalePixelFormat = pixelFormat;
147+
return 0;
148+
}
149+
150+
int
151+
MediaDecoder::seek(double timestamp)
152+
{
153+
int intRet;
154+
int streamCount = this->getStreamCount();
155+
int failCount = 0;
156+
for (int i = 0; i < streamCount; ++i) {
157+
double timeBase = av_q2d(mFormatContext->streams[i]->time_base);
158+
int64_t ts = static_cast<int64_t>(timestamp / timeBase);
159+
intRet = av_seek_frame(mFormatContext, i, ts, 0);
160+
if (0 > intRet) {
161+
failCount ++;
162+
}
163+
}
164+
return failCount;
165+
}
166+
167+
int
168+
MediaDecoder::seekToIndex(int streamIndex, int index) {
169+
AVStream* stream = this->getStream(streamIndex);
170+
if (!stream) { return -1; }
171+
172+
AVIndexEntry* entries = stream->index_entries;
173+
if (!entries) { return -1; }
174+
175+
if (0 < index || index <= stream->nb_index_entries) {
176+
return -1;
177+
}
178+
179+
AVIndexEntry* entry = &entries[index];
180+
int ret = url_fseek(mFormatContext->pb, entry->pos, SEEK_SET);
181+
if (ret < 0) { return ret; }
182+
av_update_cur_dts(mFormatContext, stream, entry->timestamp);
183+
return 0;
184+
}
185+
186+
int
187+
MediaDecoder::readPacket(AVPacket* packet)
188+
{
189+
int ret = av_read_frame(mFormatContext, packet);
190+
if (ret == AVERROR_EOF) { return ret; }
191+
if (ret < 0) {
192+
this->outputMessage(0, "av_read_frame() failed: %d", ret);
193+
}
194+
return ret;
195+
}
196+
197+
double
198+
MediaDecoder::getVideoTimestamp(AVPacket* packet, AVFrame* frame)
199+
{
200+
AVStream* stream = mFormatContext->streams[packet->stream_index];
201+
int64_t pts = 0;
202+
if (frame->reordered_opaque != (signed)AV_NOPTS_VALUE) {
203+
pts = frame->reordered_opaque;
204+
} else if (packet->dts != (signed)AV_NOPTS_VALUE) {
205+
pts = packet->dts;
206+
}
207+
208+
double timeBase = av_q2d(stream->time_base);
209+
return pts * timeBase;
210+
}
211+
212+
int
213+
MediaDecoder::decodeVideoFrame(AVPacket* packet, AVFrame* outFrame, double* outTimestamp)
214+
{
215+
int intRet;
216+
int gotPicture;
217+
int streamIndex = packet->stream_index;
218+
AVCodecContext* codecContext = mFormatContext->streams[streamIndex]->codec;
219+
220+
codecContext->reordered_opaque = packet->pts;
221+
222+
intRet = avcodec_decode_video2(codecContext, outFrame, &gotPicture, packet);
223+
if (0 > intRet) { return intRet; }
224+
225+
if (!gotPicture) { return 1; }
226+
227+
if (outTimestamp) {
228+
*outTimestamp = this->getVideoTimestamp(packet, outFrame);
229+
}
230+
231+
return 0;
232+
}
233+
234+
int
235+
MediaDecoder::scaleVideoFrame(AVFrame* frame, uint8_t* buffer, int bufferSize)
236+
{
237+
int intRet;
238+
uint8_t* data[] = { (uint8_t*)buffer, NULL, NULL, NULL };
239+
int lineSize[] = { mScaleStride, 0, 0, 0 };
240+
if (bufferSize < this->getScaleBufferSize()) {
241+
this->outputMessage(0, "Insufficient buffer size (%d). %d is required.", bufferSize, this->getScaleBufferSize());
242+
return -1;
243+
}
244+
245+
intRet = sws_scale(
246+
mSwsContext,
247+
frame->data, // src
248+
frame->linesize, // src stride
249+
0, // src slice Y
250+
mScaleHeight, // src slice H
251+
data, // dst
252+
lineSize // dst stride
253+
);
254+
return intRet;
255+
}
256+
257+
int
258+
MediaDecoder::decodeVideo(AVPacket* packet, uint8_t* buffer, int bufferSize, double thresholdTimestamp, double* outTimestamp, int* outDataSize, bool* outSkipped)
259+
{
260+
int intRet;
261+
int result = 0;
262+
double timestamp;
263+
264+
*outTimestamp = 0.0;
265+
*outDataSize = 0;
266+
*outSkipped = false;
267+
268+
269+
// Decode the next frame
270+
intRet = this->decodeVideoFrame(packet, mFrame, &timestamp);
271+
if (intRet != 0) {
272+
result = intRet;
273+
goto CLEANUP;
274+
}
275+
276+
if (timestamp < thresholdTimestamp) {
277+
*outSkipped = true;
278+
result = 1;
279+
goto CLEANUP;
280+
}
281+
282+
283+
// scale and transform
284+
intRet = this->scaleVideoFrame(mFrame, buffer, bufferSize);
285+
if (intRet < 0) {
286+
result = intRet;
287+
goto CLEANUP;
288+
}
289+
290+
*outTimestamp = timestamp;
291+
*outDataSize = mScaleHeight * mScaleStride;
292+
293+
CLEANUP:
294+
return result;
295+
}
296+
297+
double
298+
MediaDecoder::getAudioTimestamp(const AVPacket* packet)
299+
{
300+
AVStream* stream = mFormatContext->streams[packet->stream_index];
301+
double timeBase = av_q2d(stream->time_base);
302+
if (packet->pts == (signed)AV_NOPTS_VALUE) {
303+
return 0.0;
304+
} else {
305+
return packet->pts * timeBase;
306+
}
307+
}
308+
309+
int
310+
MediaDecoder::decodeAudio(AVPacket* packet, uint8_t* buffer, int bufferSize, double* outTimestamp, int* outDataSize)
311+
{
312+
int readCount = 0;
313+
AVCodecContext* codecContext = mFormatContext->streams[packet->stream_index]->codec;
314+
315+
while (packet->size > 0) {
316+
int bufSize = bufferSize - readCount;
317+
318+
int size = avcodec_decode_audio3(codecContext, (int16_t*)(buffer + readCount), &bufSize, packet);
319+
if (size == 0) { break; }
320+
if (size < 0) {
321+
this->outputMessage(0, "avcodec_decode_audio3() failed: %d", size);
322+
break;
323+
}
324+
325+
if (bufSize < 0) { continue; }
326+
327+
packet->size -= size;
328+
readCount += bufSize;
329+
}
330+
331+
double timestamp = this->getAudioTimestamp(packet);
332+
*outTimestamp = timestamp;
333+
*outDataSize = readCount;
334+
335+
return 0;
336+
}
337+
338+
int
339+
MediaDecoder::prepareStream(int streamIndex)
340+
{
341+
int intRet;
342+
343+
AVStream* stream = mFormatContext->streams[streamIndex];
344+
AVCodecContext* codecContext = stream->codec;
345+
AVCodec* codec = avcodec_find_decoder(codecContext->codec_id);
346+
347+
if (!codec) {
348+
this->outputMessage(0, "Failed to find decoder: codec_id=%d", codecContext->codec_id);
349+
return -1;
350+
}
351+
352+
// Handle truncated bitstreams
353+
if (codec->capabilities & CODEC_CAP_TRUNCATED) {
354+
codecContext->flags |= CODEC_FLAG_TRUNCATED;
355+
}
356+
357+
codecContext->debug = 0;
358+
codecContext->debug_mv = 0;
359+
codecContext->lowres = 0; // lowres decoding: 1->1/2 2->1/4
360+
codecContext->workaround_bugs = 1;
361+
codecContext->idct_algo = FF_IDCT_AUTO;
362+
codecContext->skip_frame = AVDISCARD_DEFAULT;
363+
codecContext->skip_idct = AVDISCARD_DEFAULT;
364+
codecContext->skip_loop_filter = AVDISCARD_DEFAULT;
365+
codecContext->error_recognition = FF_ER_CAREFUL;
366+
codecContext->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK;
367+
368+
// open the codec.
369+
if (0 > (intRet = avcodec_open(codecContext, codec))) {
370+
this->outputMessage(0, "avcodec_open() failed: %d", intRet);
371+
return intRet;
372+
}
373+
374+
return 0;
375+
}
376+
377+

0 commit comments

Comments
 (0)