Audacity 3.2.0
AVCodecContextWrapper.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 AVCodecContextWrapper.cpp
6
7 Dmitry Vedenko
8
9**********************************************************************/
10
12
13#include <cstring>
14
15#include "FFmpegFunctions.h"
16#include "AVCodecWrapper.h"
17
19 const FFmpegFunctions& ffmpeg, std::unique_ptr<AVCodecWrapper> codec) noexcept
20 : mFFmpeg(ffmpeg)
21 , mAVCodec(std::move(codec))
22 , mIsOwned(true)
23{
24 mAVCodecContext =
25 mFFmpeg.avcodec_alloc_context3(mAVCodec->GetWrappedValue());
26}
27
29 const FFmpegFunctions& ffmpeg, AVCodecContext* wrapped) noexcept
30 : mFFmpeg(ffmpeg)
31 , mAVCodecContext(wrapped)
32 , mIsOwned(false)
33{
34
35}
36
38{
39 return mAVCodecContext;
40}
41
43{
44 return mAVCodecContext;
45}
46
48{
49 if (mIsOwned && mAVCodecContext != nullptr)
50 {
51 // avcodec_free_context, complementary to avcodec_alloc_context3, is
52 // not necessarily loaded
53 if (mFFmpeg.avcodec_free_context != nullptr)
54 {
56 }
57 else
58 {
59 // Its not clear how to avoid the leak here, but let's close
60 // the codec at least
63 }
64 }
65}
66
67std::vector<uint8_t>
69{
70 auto frame = mFFmpeg.CreateAVFrameWrapper();
71 std::vector<uint8_t> data;
72
73 if (mFFmpeg.avcodec_send_packet == nullptr)
74 {
75 std::unique_ptr<AVPacketWrapper> packetCopy =
76 packet ? packet->Clone() : mFFmpeg.CreateAVPacketWrapper();
77
78 /*
79 "Flushing is done by calling this function [avcodec_decode_audio4]
80 with packets with avpkt->data set to NULL and avpkt->size set to 0
81 until it stops returning samples."
82 (That implies, not necessarily just one loop pass to flush)
83 */
84 bool flushing = packet
85 ? (packetCopy->GetSize() == 0 && packetCopy->GetData() == nullptr)
86 : true;
87 if (!flushing && packetCopy->GetData() == nullptr)
88 return {};
89
90 int bytesDecoded = 0;
91
92 do
93 {
94 int gotFrame;
95 // Deprecated? https://ffmpeg.org/doxygen/3.3/group__lavc__decoding.html#gaaa1fbe477c04455cdc7a994090100db4
96 bytesDecoded = mFFmpeg.avcodec_decode_audio4(
97 mAVCodecContext, frame->GetWrappedValue(), &gotFrame,
98 packetCopy->GetWrappedValue());
99
100 if (bytesDecoded < 0)
101 return data; // Packet decoding has failed
102
103 if (gotFrame == 0)
104 {
105 /*
106 "Note that this field being set to zero does not mean that an
107 error has occurred. For decoders with AV_CODEC_CAP_DELAY set, no
108 given decode call is guaranteed to produce a frame."
109 */
110 // (Let's assume this doesn't happen when flushing)
111 // Still, the data was consumed by the decoder, so we need to
112 // offset the packet
113 packetCopy->OffsetPacket(bytesDecoded);
114 continue;
115 }
116
117 ConsumeFrame(data, *frame);
118
119 packetCopy->OffsetPacket(bytesDecoded);
120 }
121 while ( flushing ? bytesDecoded > 0 : packetCopy->GetSize() > 0 );
122 }
123 else
124 {
125 auto ret = mFFmpeg.avcodec_send_packet(
127 packet != nullptr ? packet->GetWrappedValue() : nullptr);
128
129 if (ret < 0)
130 // send_packet has failed
131 return data;
132
133 while (ret >= 0)
134 {
135 ret = mFFmpeg.avcodec_receive_frame(mAVCodecContext, frame->GetWrappedValue());
136 if (ret == AUDACITY_AVERROR(EAGAIN) || ret == AUDACITY_AVERROR_EOF)
137 // The packet is fully consumed OR more data is needed
138 break;
139 else if (ret < 0)
140 // Decoding has failed
141 return data;
142
143 ConsumeFrame(data, *frame);
144 }
145 }
146
147 return data;
148}
149
151 std::vector<uint8_t>& data, AVFrameWrapper& frame)
152{
153 const int channels = GetChannels();
154
155 const auto sampleSize = static_cast<size_t>(mFFmpeg.av_get_bytes_per_sample(
156 static_cast<AVSampleFormatFwd>(frame.GetFormat())));
157
158 const auto samplesCount = frame.GetSamplesCount();
159 const auto frameSize = channels * sampleSize * samplesCount;
160
161 auto oldSize = data.size();
162 data.resize(oldSize + frameSize);
163 auto pData = &data[oldSize];
164
165 if (frame.GetData(1) != nullptr)
166 {
167 // We return interleaved buffer
168 for (int channel = 0; channel < channels; channel++)
169 {
170 for (int sample = 0; sample < samplesCount; sample++)
171 {
172 const uint8_t* channelData =
173 frame.GetExtendedData(channel) + sampleSize * sample;
174
175 uint8_t* output =
176 pData + sampleSize * (channels * sample + channel);
177
178 std::copy(channelData, channelData + sampleSize, output);
179 }
180 }
181 }
182 else
183 {
184 uint8_t* frameData = frame.GetData(0);
185 std::copy(frameData, frameData + frameSize, pData);
186 }
187}
188
189namespace
190{
191unsigned int MakeTag(char a, char b, char c, char d) noexcept
192{
193 return
194 (static_cast<unsigned>(a) << 0) | (static_cast<unsigned>(b) << 8) |
195 (static_cast<unsigned>(c) << 16) | (static_cast<unsigned>(d) << 24);
196}
197}
198
199void AVCodecContextWrapper::SetCodecTagFourCC(const char* fourCC) noexcept
200{
201 if (fourCC == nullptr || std::strlen(fourCC) != 4)
202 return;
203
204 SetCodecTag(MakeTag(fourCC[0], fourCC[1], fourCC[2], fourCC[3]));
205}
#define AUDACITY_AVERROR(e)
Definition: FFmpegTypes.h:28
#define AUDACITY_AVERROR_EOF
Definition: FFmpegTypes.h:32
int AVSampleFormatFwd
Definition: FFmpegTypes.h:132
AVCodecContext * mAVCodecContext
AVCodecContext * GetWrappedValue() noexcept
const FFmpegFunctions & mFFmpeg
AVCodecContextWrapper(const AVCodecContextWrapper &)=delete
virtual int GetChannels() const noexcept=0
std::vector< uint8_t > DecodeAudioPacket(const AVPacketWrapper *packet)
void SetCodecTagFourCC(const char *fourCC) noexcept
void ConsumeFrame(std::vector< uint8_t > &data, AVFrameWrapper &frame)
virtual AVSampleFormatFwd GetFormat() const noexcept=0
virtual int GetSamplesCount() const noexcept=0
virtual uint8_t * GetData(int index) const noexcept=0
virtual uint8_t * GetExtendedData(int index) const noexcept=0
AVPacket * GetWrappedValue() noexcept
virtual std::unique_ptr< AVPacketWrapper > Clone() const noexcept=0
unsigned int MakeTag(char a, char b, char c, char d) noexcept
void copy(const T *src, T *dst, int32_t n)
Definition: VectorOps.h:40
int(* avcodec_close)(AVCodecContext *avctx)
int(* avcodec_receive_frame)(AVCodecContext *avctx, AVFrame *frame)
int(* avcodec_send_packet)(AVCodecContext *avctx, const AVPacket *avpkt)
int(* avcodec_is_open)(AVCodecContext *avctx)
int(* avcodec_decode_audio4)(AVCodecContext *avctx, AVFrame *frame, int *got_output, const AVPacket *avpkt)
void(* avcodec_free_context)(AVCodecContext **avctx)
int(* av_get_bytes_per_sample)(AVSampleFormatFwd sample_fmt)
std::unique_ptr< AVFrameWrapper > CreateAVFrameWrapper() const
std::unique_ptr< AVPacketWrapper > CreateAVPacketWrapper() const