[cosmetics] update date in GPL header
[vuplus_xbmc] / xbmc / cdrip / EncoderFFmpeg.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #ifndef __STDC_CONSTANT_MACROS
22 #define __STDC_CONSTANT_MACROS
23 #endif
24 #ifndef __STDC_LIMIT_MACROS
25 #define __STDC_LIMIT_MACROS
26 #endif
27 #ifdef _LINUX
28 #include "stdint.h"
29 #else
30 #define INT64_C __int64
31 #endif
32
33 #include "EncoderFFmpeg.h"
34 #include "filesystem/File.h"
35 #include "utils/log.h"
36 #include "settings/GUISettings.h"
37 #include "utils/URIUtils.h"
38
39 /* AV_PKT_FLAG_KEY was named PKT_FLAG_KEY in older versions of libavcodec */
40 #ifndef AV_PKT_FLAG_KEY
41 #define AV_PKT_FLAG_KEY PKT_FLAG_KEY
42 #endif
43
44 CEncoderFFmpeg::CEncoderFFmpeg():
45   m_Format    (NULL),
46   m_CodecCtx  (NULL),
47   m_Stream    (NULL),
48   m_Buffer    (NULL),
49   m_BufferSize(0)
50 {
51 }
52
53 bool CEncoderFFmpeg::Init(const char* strFile, int iInChannels, int iInRate, int iInBits)
54 {
55   if (!m_dllAvUtil.Load() || !m_dllAvCodec.Load() || !m_dllAvFormat.Load()) return false;
56   m_dllAvFormat.av_register_all();
57   m_dllAvCodec.avcodec_register_all();
58
59   CStdString filename = URIUtils::GetFileName(strFile);
60   AVOutputFormat *fmt = NULL;
61   fmt = m_dllAvFormat.av_guess_format(NULL, filename.c_str(), NULL);
62   if (!fmt)
63   {
64     CLog::Log(LOGERROR, "CEncoderFFmpeg::Init - Unable to guess the output format for the file %s", filename.c_str());
65     return false;
66   }
67
68   AVCodec *codec;
69   codec = m_dllAvCodec.avcodec_find_encoder(
70     strcmp(fmt->name, "ogg") == 0 ? CODEC_ID_VORBIS : fmt->audio_codec
71   );
72
73   if (!codec)
74   {
75     CLog::Log(LOGERROR, "CEncoderFFmpeg::Init - Unable to find a suitable FFmpeg encoder");
76     return false;
77   }
78
79   m_Format     = m_dllAvFormat.avformat_alloc_context();
80   m_Format->pb = m_dllAvFormat.avio_alloc_context(m_BCBuffer, sizeof(m_BCBuffer), AVIO_FLAG_READ, this,  NULL, MuxerReadPacket, NULL);
81   if (!m_Format->pb)
82   {
83     m_dllAvUtil.av_freep(&m_Format);
84     CLog::Log(LOGERROR, "CEncoderFFmpeg::Init - Failed to allocate ByteIOContext");
85     return false;
86   }
87
88   m_Format->oformat  = fmt;
89   m_Format->bit_rate = g_guiSettings.GetInt("audiocds.bitrate") * 1000;
90
91   /* add a stream to it */
92   m_Stream = m_dllAvFormat.avformat_new_stream(m_Format, codec);
93   if (!m_Stream)
94   {
95     m_dllAvUtil.av_freep(&m_Format->pb);
96     m_dllAvUtil.av_freep(&m_Format);
97     CLog::Log(LOGERROR, "CEncoderFFmpeg::Init - Failed to allocate AVStream context");
98     return false;
99   }
100
101   /* set the stream's parameters */
102   m_CodecCtx                 = m_Stream->codec;
103   m_CodecCtx->codec_id       = codec->id;
104   m_CodecCtx->codec_type     = AVMEDIA_TYPE_AUDIO;
105   m_CodecCtx->bit_rate       = m_Format->bit_rate;
106   m_CodecCtx->sample_rate    = iInRate;
107   m_CodecCtx->channels       = iInChannels;
108   m_CodecCtx->channel_layout = m_dllAvUtil.av_get_default_channel_layout(iInChannels);
109   m_CodecCtx->time_base      = (AVRational){1, iInRate};
110
111   if(fmt->flags & AVFMT_GLOBALHEADER)
112   {
113     m_CodecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER;
114     m_Format->flags   |= CODEC_FLAG_GLOBAL_HEADER;
115   }
116
117   m_dllAvCodec.av_init_packet(&m_Pkt);
118   m_Pkt.stream_index = m_Stream->index;
119   m_Pkt.flags       |= AV_PKT_FLAG_KEY;
120
121   switch(iInBits)
122   {
123     case  8: m_CodecCtx->sample_fmt = AV_SAMPLE_FMT_U8 ; break;
124     case 16: m_CodecCtx->sample_fmt = AV_SAMPLE_FMT_S16; break;
125     case 32: m_CodecCtx->sample_fmt = AV_SAMPLE_FMT_S32; break;
126     default:
127       m_dllAvUtil.av_freep(&m_Stream);
128       m_dllAvUtil.av_freep(&m_Format->pb);
129       m_dllAvUtil.av_freep(&m_Format);
130       return false;
131   }
132
133   if (m_dllAvCodec.avcodec_open2(m_CodecCtx, codec, NULL))
134   {
135     CLog::Log(LOGERROR, "CEncoderFFmpeg::Init - Failed to open the codec");
136     m_dllAvUtil.av_freep(&m_Stream);
137     m_dllAvUtil.av_freep(&m_Format->pb);
138     m_dllAvUtil.av_freep(&m_Format);
139     return false;
140   }
141
142   /* calculate how many bytes we need per frame */
143   m_NeededFrames = m_CodecCtx->frame_size;
144   m_NeededBytes  = m_NeededFrames * iInChannels * (iInBits / 8);
145   m_Buffer       = new uint8_t[m_NeededBytes];
146   m_BufferSize   = 0;
147
148   /* set input stream information and open the file */
149   if (!CEncoder::Init(strFile, iInChannels, iInRate, iInBits))
150   {
151     CLog::Log(LOGERROR, "CEncoderFFmpeg::Init - Failed to call CEncoder::Init");
152     delete[] m_Buffer;
153     m_dllAvUtil.av_freep(&m_Stream);
154     m_dllAvUtil.av_freep(&m_Format->pb);
155     m_dllAvUtil.av_freep(&m_Format);
156     return false;
157   }
158
159   /* set the tags */
160   SetTag("album"       , m_strAlbum);
161   SetTag("album_artist", m_strArtist);
162   SetTag("genre"       , m_strGenre);
163   SetTag("title"       , m_strTitle);
164   SetTag("track"       , m_strTrack);
165   SetTag("encoder"     , "XBMC FFmpeg Encoder");
166
167   /* write the header */
168   if (m_dllAvFormat.avformat_write_header(m_Format, NULL) != 0)
169   {
170     CLog::Log(LOGERROR, "CEncoderFFmpeg::Init - Failed to write the header");
171     delete[] m_Buffer;
172     m_dllAvUtil.av_freep(&m_Stream);
173     m_dllAvUtil.av_freep(&m_Format->pb);
174     m_dllAvUtil.av_freep(&m_Format);
175     return false;
176   }
177
178   return true;
179 }
180
181 void CEncoderFFmpeg::SetTag(const CStdString tag, const CStdString value)
182 {
183   m_dllAvUtil.av_dict_set(&m_Format->metadata, tag.c_str(), value.c_str(), 0);
184 }
185
186 int CEncoderFFmpeg::MuxerReadPacket(void *opaque, uint8_t *buf, int buf_size)
187 {
188   CEncoderFFmpeg *enc = (CEncoderFFmpeg*)opaque;
189   if(enc->WriteStream(buf, buf_size) != buf_size)
190   {
191     CLog::Log(LOGERROR, "Error writing FFmpeg buffer to file");
192     return -1;
193   }
194   return buf_size;
195 }
196
197 int CEncoderFFmpeg::Encode(int nNumBytesRead, BYTE* pbtStream)
198 {
199   while(nNumBytesRead > 0)
200   {
201     unsigned int space = m_NeededBytes - m_BufferSize;
202     unsigned int copy  = (unsigned int)nNumBytesRead > space ? space : nNumBytesRead;
203
204     memcpy(&m_Buffer[m_BufferSize], pbtStream, space);
205     m_BufferSize  += copy;
206     pbtStream     += copy;
207     nNumBytesRead -= copy;
208
209     /* only write full packets */
210     if (m_BufferSize == m_NeededBytes)
211       if (!WriteFrame()) return 0;
212   }
213
214   return 1;
215 }
216
217 bool CEncoderFFmpeg::WriteFrame()
218 {
219   uint8_t outbuf[FF_MIN_BUFFER_SIZE];
220   int encoded;
221
222   encoded = m_dllAvCodec.avcodec_encode_audio(m_CodecCtx, outbuf, FF_MIN_BUFFER_SIZE, (short*)m_Buffer);
223   m_BufferSize = 0;
224   if (encoded < 0) {
225     CLog::Log(LOGERROR, "CEncoderFFmpeg::WriteFrame - Error encoding audio");
226     return false;
227   }
228
229   m_Pkt.data = outbuf;
230   m_Pkt.size = encoded;
231
232   if (m_CodecCtx->coded_frame && (uint64_t)m_CodecCtx->coded_frame->pts != AV_NOPTS_VALUE)
233     m_Pkt.pts = m_dllAvUtil.av_rescale_q(m_CodecCtx->coded_frame->pts, m_Stream->time_base, m_CodecCtx->time_base);
234
235   if (m_dllAvFormat.av_write_frame(m_Format, &m_Pkt) < 0) {
236     CLog::Log(LOGERROR, "CEncoderFFMmpeg::WriteFrame - Failed to write the frame data");
237     return false;
238   }
239
240   return true;
241 }
242
243 bool CEncoderFFmpeg::Close()
244 {
245   if (m_Format) {
246     /* if there is anything still in the buffer */
247     if (m_BufferSize > 0) {
248       /* zero the unused space so we dont encode random junk */
249       memset(&m_Buffer[m_BufferSize], 0, m_NeededBytes - m_BufferSize);
250       /* write any remaining data */
251       WriteFrame();
252     }
253
254     /* write the eof flag */
255     delete[] m_Buffer;
256     m_Buffer = NULL;
257     WriteFrame();
258
259     /* write the trailer */
260     m_dllAvFormat.av_write_trailer(m_Format);
261     FlushStream();
262     FileClose();
263
264     /* cleanup */
265     m_dllAvCodec.avcodec_close(m_CodecCtx);
266     m_dllAvUtil.av_freep(&m_Stream    );
267     m_dllAvUtil.av_freep(&m_Format->pb);
268     m_dllAvUtil.av_freep(&m_Format    );
269   }
270
271   m_BufferSize = 0;
272
273   m_dllAvFormat.Unload();
274   m_dllAvUtil  .Unload();
275   m_dllAvCodec .Unload();
276   return true;
277 }
278