1 /*
2  *  Copyright (C) 2005-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "Encoder.h"
10 
11 #include "filesystem/File.h"
12 #include "utils/log.h"
13 
14 #include <string.h>
15 #include <utility>
16 
CEncoder(std::shared_ptr<IEncoder> encoder)17 CEncoder::CEncoder(std::shared_ptr<IEncoder> encoder)
18 {
19   m_file = NULL;
20   m_dwWriteBufferPointer = 0;
21   m_impl = std::move(encoder);
22 }
23 
~CEncoder()24 CEncoder::~CEncoder()
25 {
26   FileClose();
27 }
28 
WriteCallback(void * opaque,const uint8_t * data,int size)29 int CEncoder::WriteCallback(void *opaque, const uint8_t *data, int size)
30 {
31   if (opaque)
32   {
33     CEncoder *encoder = static_cast<CEncoder *>(opaque);
34     return encoder->WriteStream(data, size);
35   }
36   return -1;
37 }
38 
SeekCallback(void * opaque,int64_t position,int whence)39 int64_t CEncoder::SeekCallback(void *opaque, int64_t position, int whence)
40 {
41   if (opaque)
42   {
43     CEncoder *encoder = static_cast<CEncoder *>(opaque);
44     return encoder->FileSeek(position, whence);
45   }
46   return -1;
47 }
48 
Init(const char * strFile,int iInChannels,int iInRate,int iInBits)49 bool CEncoder::Init(const char* strFile, int iInChannels, int iInRate, int iInBits)
50 {
51   if (strFile == NULL) return false;
52 
53   m_dwWriteBufferPointer = 0;
54   m_impl->m_strFile = strFile;
55 
56   m_impl->m_iInChannels = iInChannels;
57   m_impl->m_iInSampleRate = iInRate;
58   m_impl->m_iInBitsPerSample = iInBits;
59 
60   if (!FileCreate(strFile))
61   {
62     CLog::Log(LOGERROR, "Error: Cannot open file: %s", strFile);
63     return false;
64   }
65 
66   AddonToKodiFuncTable_AudioEncoder callbacks;
67   callbacks.kodiInstance = this;
68   callbacks.write = WriteCallback;
69   callbacks.seek = SeekCallback;
70   return m_impl->Init(callbacks);
71 }
72 
FileCreate(const char * filename)73 bool CEncoder::FileCreate(const char* filename)
74 {
75   delete m_file;
76 
77   m_file = new XFILE::CFile;
78   if (m_file)
79     return m_file->OpenForWrite(filename, true);
80   return false;
81 }
82 
FileClose()83 bool CEncoder::FileClose()
84 {
85   if (m_file)
86   {
87     m_file->Close();
88     delete m_file;
89     m_file = NULL;
90   }
91   return true;
92 }
93 
94 // return total bytes written, or -1 on error
FileWrite(const void * pBuffer,uint32_t iBytes)95 int CEncoder::FileWrite(const void *pBuffer, uint32_t iBytes)
96 {
97   if (!m_file)
98     return -1;
99 
100   ssize_t dwBytesWritten = m_file->Write(pBuffer, iBytes);
101   if (dwBytesWritten <= 0)
102     return -1;
103 
104   return dwBytesWritten;
105 }
106 
FileSeek(int64_t iFilePosition,int iWhence)107 int64_t CEncoder::FileSeek(int64_t iFilePosition, int iWhence)
108 {
109   if (!m_file)
110     return -1;
111   FlushStream();
112   return m_file->Seek(iFilePosition, iWhence);
113 }
114 
115 // write the stream to our writebuffer, and write the buffer to disk if it's full
WriteStream(const void * pBuffer,uint32_t iBytes)116 int CEncoder::WriteStream(const void *pBuffer, uint32_t iBytes)
117 {
118   if ((WRITEBUFFER_SIZE - m_dwWriteBufferPointer) > iBytes)
119   {
120     // writebuffer is big enough to fit data
121     memcpy(m_btWriteBuffer + m_dwWriteBufferPointer, pBuffer, iBytes);
122     m_dwWriteBufferPointer += iBytes;
123     return iBytes;
124   }
125   else
126   {
127     // buffer is not big enough to fit data
128     if (m_dwWriteBufferPointer == 0)
129     {
130       // nothing in our buffer, just write the entire pBuffer to disk
131       return FileWrite(pBuffer, iBytes);
132     }
133 
134     uint32_t dwBytesRemaining = iBytes - (WRITEBUFFER_SIZE - m_dwWriteBufferPointer);
135     // fill up our write buffer and write it to disk
136     memcpy(m_btWriteBuffer + m_dwWriteBufferPointer, pBuffer, (WRITEBUFFER_SIZE - m_dwWriteBufferPointer));
137     FileWrite(m_btWriteBuffer, WRITEBUFFER_SIZE);
138     m_dwWriteBufferPointer = 0;
139 
140     // pbtRemaining = pBuffer + bytesWritten
141     const uint8_t* pbtRemaining = (const uint8_t *)pBuffer + (iBytes - dwBytesRemaining);
142     if (dwBytesRemaining > WRITEBUFFER_SIZE)
143     {
144       // data is not going to fit in our buffer, just write it to disk
145       if (FileWrite(pbtRemaining, dwBytesRemaining) == -1) return -1;
146       return iBytes;
147     }
148     else
149     {
150       // copy remaining bytes to our currently empty writebuffer
151       memcpy(m_btWriteBuffer, pbtRemaining, dwBytesRemaining);
152       m_dwWriteBufferPointer = dwBytesRemaining;
153       return iBytes;
154     }
155   }
156 }
157 
158 // flush the contents of our writebuffer
FlushStream()159 int CEncoder::FlushStream()
160 {
161   int iResult;
162   if (m_dwWriteBufferPointer == 0) return 0;
163 
164   iResult = FileWrite(m_btWriteBuffer, m_dwWriteBufferPointer);
165   m_dwWriteBufferPointer = 0;
166 
167   return iResult;
168 }
169 
Encode(int nNumBytesRead,uint8_t * pbtStream)170 int CEncoder::Encode(int nNumBytesRead, uint8_t* pbtStream)
171 {
172   int iBytes = m_impl->Encode(nNumBytesRead, pbtStream);
173 
174   if (iBytes < 0)
175   {
176     CLog::Log(LOGERROR, "Internal encoder error: %i", iBytes);
177     return 0;
178   }
179   return 1;
180 }
181 
CloseEncode()182 bool CEncoder::CloseEncode()
183 {
184   if (!m_impl->Close())
185     return false;
186 
187   FlushStream();
188   FileClose();
189 
190   return true;
191 }
192