1 /**********************************************************************
2 
3   Audacity: A Digital Audio Editor
4 
5   AVCodecContextWrapper.cpp
6 
7   Dmitry Vedenko
8 
9 **********************************************************************/
10 
11 #include "AVCodecContextWrapper.h"
12 
13 #include <cstring>
14 
15 #include "FFmpegFunctions.h"
16 #include "AVCodecWrapper.h"
17 
AVCodecContextWrapper(const FFmpegFunctions & ffmpeg,std::unique_ptr<AVCodecWrapper> codec)18 AVCodecContextWrapper::AVCodecContextWrapper(
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 
AVCodecContextWrapper(const FFmpegFunctions & ffmpeg,AVCodecContext * wrapped)28 AVCodecContextWrapper::AVCodecContextWrapper(
29    const FFmpegFunctions& ffmpeg, AVCodecContext* wrapped) noexcept
30     : mFFmpeg(ffmpeg)
31     , mAVCodecContext(wrapped)
32     , mIsOwned(false)
33 {
34 
35 }
36 
GetWrappedValue()37 AVCodecContext* AVCodecContextWrapper::GetWrappedValue() noexcept
38 {
39     return mAVCodecContext;
40 }
41 
GetWrappedValue() const42 const AVCodecContext* AVCodecContextWrapper::GetWrappedValue() const noexcept
43 {
44    return mAVCodecContext;
45 }
46 
~AVCodecContextWrapper()47 AVCodecContextWrapper::~AVCodecContextWrapper()
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       {
55          mFFmpeg.avcodec_free_context(&mAVCodecContext);
56       }
57       else
58       {
59          // Its not clear how to avoid the leak here, but let's close
60          // the codec at least
61          if (mFFmpeg.avcodec_is_open(mAVCodecContext))
62             mFFmpeg.avcodec_close(mAVCodecContext);
63       }
64    }
65 }
66 
67 std::vector<uint8_t>
DecodeAudioPacket(const AVPacketWrapper * packet)68 AVCodecContextWrapper::DecodeAudioPacket(const AVPacketWrapper* packet)
69 {
70    auto frame = mFFmpeg.CreateAVFrameWrapper();
71    std::vector<uint8_t> data;
72 
73    if (mFFmpeg.avcodec_decode_audio4)
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       const int channels = GetChannels();
91 
92       int bytesDecoded = 0;
93       do
94       {
95          int gotFrame;
96          // Deprecated?  https://ffmpeg.org/doxygen/3.3/group__lavc__decoding.html#gaaa1fbe477c04455cdc7a994090100db4
97          bytesDecoded = mFFmpeg.avcodec_decode_audio4(
98             mAVCodecContext, frame->GetWrappedValue(), &gotFrame,
99             packetCopy->GetWrappedValue());
100 
101          if (bytesDecoded < 0)
102             return data; // Packet decoding has failed
103 
104          if (gotFrame == 0)
105          {
106             /*
107              "Note that this field being set to zero does not mean that an
108              error has occurred. For decoders with AV_CODEC_CAP_DELAY set, no
109              given decode call is guaranteed to produce a frame."
110              */
111             // (Let's assume this doesn't happen when flushing)
112             // Still, the data was consumed by the decoder, so we need to
113             // offset the packet
114             packetCopy->OffsetPacket(bytesDecoded);
115             continue;
116          }
117 
118          const auto sampleSize =
119             static_cast<size_t>(mFFmpeg.av_get_bytes_per_sample(
120                static_cast<AVSampleFormatFwd>(frame->GetFormat())));
121 
122          const auto samplesCount = frame->GetSamplesCount();
123          const auto frameSize = channels * sampleSize * samplesCount;
124 
125          auto oldSize = data.size();
126          data.resize(oldSize + frameSize);
127          auto pData = &data[oldSize];
128 
129          if (frame->GetData(1) != nullptr)
130          {
131             // We return interleaved buffer
132             for (int channel = 0; channel < channels; channel++)
133             {
134                for (int sample = 0; sample < samplesCount; sample++)
135                {
136                   const uint8_t* channelData =
137                      frame->GetExtendedData(channel) + sampleSize * sample;
138 
139                   uint8_t* output =
140                      pData + sampleSize * (channels * sample + channel);
141 
142                   std::copy(channelData, channelData + sampleSize, output);
143                }
144             }
145          }
146          else
147          {
148             uint8_t* frameData = frame->GetData(0);
149             std::copy(frameData, frameData + frameSize, pData);
150          }
151 
152          packetCopy->OffsetPacket(bytesDecoded);
153       }
154       while ( flushing ? bytesDecoded > 0 : packetCopy->GetSize() > 0 );
155    }
156 
157    return data;
158 }
159 
160 namespace
161 {
MakeTag(char a,char b,char c,char d)162 unsigned int MakeTag(char a, char b, char c, char d) noexcept
163 {
164    return
165       (static_cast<unsigned>(a) << 0) | (static_cast<unsigned>(b) << 8) |
166       (static_cast<unsigned>(c) << 16) | (static_cast<unsigned>(d) << 24);
167 }
168 }
169 
SetCodecTagFourCC(const char * fourCC)170 void AVCodecContextWrapper::SetCodecTagFourCC(const char* fourCC) noexcept
171 {
172    if (fourCC == nullptr || std::strlen(fourCC) != 4)
173       return;
174 
175    SetCodecTag(MakeTag(fourCC[0], fourCC[1], fourCC[2], fourCC[3]));
176 }
177