File: FFmpegAudioReader.cpp

package info (click to toggle)
freespace2 24.2.0%2Brepack-1
  • links: PTS, VCS
  • area: non-free
  • in suites: forky, sid
  • size: 43,716 kB
  • sloc: cpp: 595,001; ansic: 21,741; python: 1,174; sh: 457; makefile: 248; xml: 181
file content (187 lines) | stat: -rw-r--r-- 4,241 bytes parent folder | download | duplicates (2)
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
176
177
178
179
180
181
182
183
184
185
186
187
#include "FFmpegAudioReader.h"

namespace
{
class AVPacketScope
{
	AVPacket* _packet;
public:
	explicit AVPacketScope(AVPacket* av_packet)
		: _packet(av_packet) {
	}

	~AVPacketScope() {
		av_packet_unref(_packet);
	}
};
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 24, 100)
// This version doesn't have the new packet alloc API

AVPacket *av_packet_alloc()
{
	auto packet = (AVPacket*)av_mallocz(sizeof(AVPacket));
	av_packet_unref(packet);
	return packet;
}

AVPacket *av_packet_clone(AVPacket *src)
{
	auto ret = av_packet_alloc();
	av_packet_ref(ret, src);
	return ret;
}

void av_packet_free(AVPacket **pkt)
{
	av_packet_unref(*pkt);
	av_freep(pkt);
}
#endif
} // namespace

namespace sound {
namespace ffmpeg {
FFmpegAudioReader::FFmpegAudioReader(AVFormatContext* av_format_context, AVCodecContext* codec_ctx,
                                     int stream_idx) : _stream_idx(stream_idx),
                                                       _format_ctx(av_format_context),
                                                       _codec_ctx(codec_ctx) {
}

bool FFmpegAudioReader::readFrame(AVFrame* decode_frame) {
	// FFmpeg 3.1 and onwards has a new, better packet handling API but that version is relatively new so we still need
	// to support the earlier API. The compatibility code can be removed once the new version is supported by most platforms

#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(57, 24, 255)
	// Process pending frames
	auto pending_res = avcodec_receive_frame(_codec_ctx, decode_frame);

	if (pending_res == 0) {
		// Got a frame
		return true;
	}
	if (pending_res == AVERROR_EOF) {
		// No more frames available
		return false;
	}

	if (pending_res != AVERROR(EAGAIN)) {
		// Unknown error.
		return false;
	}
#else
	if (_currentPacket != nullptr) {
		// Got some data left
		int finishedFrame = 0;
		auto bytes_read = avcodec_decode_audio4(_codec_ctx, decode_frame, &finishedFrame, _currentPacket);

		if (bytes_read < 0) {
			// Error!
			return false;
		}

		_currentPacket->data += bytes_read;
		_currentPacket->size -= bytes_read;

		if (_currentPacket->size <= 0) {
			// Done with this packet
			av_packet_free(&_currentPacket);
		}

		if (finishedFrame) {
			return true;
		}
	}
#endif

	AVPacket packet;
	while (av_read_frame(_format_ctx, &packet) >= 0) {
		AVPacketScope packet_scope(&packet);
		if (packet.stream_index == _stream_idx) {
#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(57, 24, 255)
			auto res = avcodec_send_packet(_codec_ctx, &packet);

			if (res != 0) {
				// Error or EOF
				return false;
			}

			res = avcodec_receive_frame(_codec_ctx, decode_frame);

			if (res == 0) {
				// Got a frame
				return true;
			}
			if (res == AVERROR_EOF) {
				// No more frames available
				return false;
			}

			if (res != AVERROR(EAGAIN)) {
				// Unknown error.
				return false;
			}
			// EGAIN was returned, send new input
#else
			int finishedFrame = 0;
			auto bytes_read = avcodec_decode_audio4(_codec_ctx, decode_frame, &finishedFrame, &packet);

			if (bytes_read < packet.size) {
				// Not all data was read
				packet.data += bytes_read;
				packet.size -= bytes_read;

				_currentPacket = av_packet_clone(&packet);
			}

			if (finishedFrame) {
				return true;
			}

			if (bytes_read < 0) {
				// Error
				return false;
			}
#endif
		}
	}

#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(57, 24, 255)
	// Flush decoder
	if (avcodec_send_packet(_codec_ctx, nullptr) != 0) {
		return false;
	}

	auto flush_res = avcodec_receive_frame(_codec_ctx, decode_frame);

	if (flush_res == 0) {
		// Got a frame
		return true;
	}
#else
	AVPacket nullPacket;
	memset(&nullPacket, 0, sizeof(nullPacket));
	nullPacket.data = nullptr;
	nullPacket.size = 0;

	int finishedFrame = 1;
	auto err = avcodec_decode_audio4(_codec_ctx, decode_frame, &finishedFrame, &nullPacket);

	if (finishedFrame && err >= 0) {
		return true;
	}
#endif

	// If we are here then read_frame reached the end or returned an error
	return false;
}
FFmpegAudioReader::~FFmpegAudioReader() {
#if LIBAVCODEC_VERSION_INT <= AV_VERSION_INT(57, 24, 255)
    if (_currentPacket != nullptr) {
		av_packet_unref(_currentPacket);
		av_packet_free(&_currentPacket);
	}
#endif
}

} // namespace ffmpeg
} // namespace sound