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