AvTranscoder  0.9.4
C++APIforLibav/FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
AudioDecoder.cpp
Go to the documentation of this file.
1 #include "AudioDecoder.hpp"
2 
6 
7 extern "C" {
8 #include <libavcodec/avcodec.h>
9 #include <libavformat/avformat.h>
10 #include <libavutil/avutil.h>
11 #include <libavutil/pixdesc.h>
12 #include <libavutil/channel_layout.h>
13 }
14 
15 #include <stdexcept>
16 #include <sstream>
17 
18 namespace avtranscoder
19 {
20 
22  : _inputStream(&inputStream)
23  , _isSetup(false)
24 {
25 }
26 
28 {
29 }
30 
32 {
33  // check the given profile
34  const bool isValid = ProfileLoader::checkAudioProfile(profile);
35  if(!isValid && !profile.empty())
36  {
37  const std::string msg("Invalid audio profile to setup decoder.");
38  LOG_ERROR(msg)
39  throw std::runtime_error(msg);
40  }
41 
42  if(!profile.empty())
43  {
44  LOG_INFO("Setup audio decoder with:\n" << profile)
45  }
46 
48 
49  // set threads before any other options
50  if(profile.count(constants::avProfileThreads))
52  else
54 
55  // set decoder options
56  for(ProfileLoader::Profile::const_iterator it = profile.begin(); it != profile.end(); ++it)
57  {
59  (*it).first == constants::avProfileType || (*it).first == constants::avProfileThreads)
60  continue;
61 
62  try
63  {
64  Option& decodeOption = codec.getOption((*it).first);
65  decodeOption.setString((*it).second);
66  }
67  catch(std::exception& e)
68  {
69  LOG_WARN("AudioDecoder - can't set option " << (*it).first << " to " << (*it).second << ": " << e.what())
70  }
71  }
72 
73  // open decoder
75  _isSetup = true;
76 }
77 
79 {
80  bool decodeNextFrame = false;
81 
82  if(!_isSetup)
83  setupDecoder();
84 
85  int got_frame = 0;
86  while(!got_frame)
87  {
88  CodedData data;
89 
90  // reading
91  const bool nextPacketRead = _inputStream->readNextPacket(data);
92 
93  // decoding
94  // @note could be called several times to return the remaining frames (last call with an empty packet)
95  // @see CODEC_CAP_DELAY
96  int ret = avcodec_decode_audio4(&_inputStream->getAudioCodec().getAVCodecContext(), &frameBuffer.getAVFrame(),
97  &got_frame, &data.getAVPacket());
98  if(ret < 0)
99  {
100  throw std::runtime_error("an error occured during audio decoding" + getDescriptionFromErrorCode(ret));
101  }
102 
103  // if no frame could be decompressed
104  if(!nextPacketRead && ret == 0 && got_frame == 0)
105  decodeNextFrame = false;
106  else
107  decodeNextFrame = true;
108 
109  // if no frame read and decompressed
110  if(!nextPacketRead && !decodeNextFrame)
111  {
112  data.clear();
113  return false;
114  }
115  }
116  return decodeNextFrame;
117 }
118 
119 bool AudioDecoder::decodeNextFrame(Frame& frameBuffer, const size_t channelIndex)
120 {
121  AudioFrame& audioBuffer = static_cast<AudioFrame&>(frameBuffer);
122 
123  // decode all data of the next frame
124  AudioFrame allDataOfNextFrame(audioBuffer);
125  if(!decodeNextFrame(allDataOfNextFrame))
126  return false;
127 
128  AVCodecContext& avCodecContext = _inputStream->getAudioCodec().getAVCodecContext();
129  const size_t srcNbChannels = avCodecContext.channels;
130  const size_t bytePerSample = av_get_bytes_per_sample((AVSampleFormat)frameBuffer.getAVFrame().format);
131 
132  const int dstNbChannels = 1;
133  const int noAlignment = 0;
134  const size_t decodedSize = av_samples_get_buffer_size(NULL, dstNbChannels, frameBuffer.getAVFrame().nb_samples,
135  avCodecContext.sample_fmt, noAlignment);
136  if(decodedSize == 0)
137  return false;
138 
139  // check if the expected channel exists
140  if(channelIndex > srcNbChannels - 1)
141  {
142  std::stringstream msg;
143  msg << "The channel at index ";
144  msg << channelIndex;
145  msg << " doesn't exist (srcNbChannels = ";
146  msg << srcNbChannels;
147  msg << ").";
148  throw std::runtime_error(msg.str());
149  }
150 
151  // copy frame properties of decoded frame
152  audioBuffer.copyProperties(allDataOfNextFrame);
153  av_frame_set_channels(&audioBuffer.getAVFrame(), 1);
154  av_frame_set_channel_layout(&audioBuffer.getAVFrame(), AV_CH_LAYOUT_MONO);
155  audioBuffer.setNbSamplesPerChannel(allDataOfNextFrame.getNbSamplesPerChannel());
156 
157  // @todo manage cases with data of frame not only on data[0] (use _frame.linesize)
158  unsigned char* src = allDataOfNextFrame.getData()[0];
159  unsigned char* dst = audioBuffer.getData()[0];
160 
161  // offset
162  src += channelIndex * bytePerSample;
163 
164  // extract one channel
165  for(int sample = 0; sample < allDataOfNextFrame.getAVFrame().nb_samples; ++sample)
166  {
167  memcpy(dst, src, bytePerSample);
168  dst += bytePerSample;
169  src += bytePerSample * srcNbChannels;
170  }
171 
172  return true;
173 }
174 
176 {
177  avcodec_flush_buffers(&_inputStream->getAudioCodec().getAVCodecContext());
178 }
179 }
bool readNextPacket(CodedData &data)
Read the next packet of the stream.
Definition: InputStream.cpp:53
AudioCodec & getAudioCodec()
Definition: InputStream.cpp:87
void setString(const std::string &value)
Definition: Option.cpp:187
#define LOG_ERROR(...)
Definition: log.hpp:35
void setupDecoder(const ProfileLoader::Profile &profile=ProfileLoader::Profile())
Setup the decoder.
void clear()
Clear existing data and set size to 0.
Definition: CodedData.cpp:71
void setInt(const int value)
Definition: Option.cpp:169
void flushDecoder()
Reset the internal decoder state / flush internal buffers.
std::string getDescriptionFromErrorCode(const int code)
Get the string description corresponding to the error code provided by ffmpeg/libav.
Definition: common.cpp:22
AVPacket & getAVPacket()
Definition: CodedData.hpp:78
std::map< std::string, std::string > Profile
const std::string avProfileIdentificator
const std::string avProfileIdentificatorHuman
void setNbSamplesPerChannel(const size_t nbSamples)
Definition: AudioFrame.hpp:54
#define LOG_INFO(...)
Definition: log.hpp:23
Option & getOption(const std::string &optionName)
Definition: ICodec.hpp:50
InputStream * _inputStream
Stream from which we read next frames (no ownership, has link)
void copyProperties(const Frame &otherFrame)
Copy all the fields that do not affect the data layout in the buffers.
Definition: Frame.cpp:57
Wrapper of AVOption. Get its type to know what the option is about: Int, Double, Ratio, Choice... Parse its array of options to get the potential childs (Choice and Group).
Definition: Option.hpp:36
unsigned char ** getData()
Get all the data of the frame.
Definition: Frame.hpp:35
#define LOG_WARN(...)
Definition: log.hpp:29
AudioDecoder(InputStream &inputStream)
static bool checkAudioProfile(const Profile &profileToCheck)
This class describes decoded audio data.
Definition: AudioFrame.hpp:36
This class describes decoded (raw) audio or video data.
Definition: Frame.hpp:16
void openCodec()
Initialize the codec context.
Definition: ICodec.cpp:56
bool decodeNextFrame(Frame &frameBuffer)
Decode next frame.
AVCodecContext & getAVCodecContext()
Definition: ICodec.hpp:53
This class describes coded data.
Definition: CodedData.hpp:18
const std::string avProfileThreads
AVFrame & getAVFrame()
Definition: Frame.hpp:88
size_t getNbSamplesPerChannel() const
Definition: AudioFrame.hpp:49
const std::string avProfileType