Changed ffmpeg log level to AV_LOG_QUIET
[platform/framework/native/media.git] / src / FMedia_Mpeg4Encoder.cpp
1 //
2 // Open Service Platform
3 // Copyright (c) 2012 Samsung Electronics Co., Ltd.
4 //
5 // Licensed under the Apache License, Version 2.0 (the License);
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17
18 #include <unique_ptr.h>
19 #include <stdio.h>
20 #include <FBaseInteger.h>
21 #include <FBaseColArrayListT.h>
22 #include <FBaseColHashMap.h>
23 #include <FMediaTypes.h>
24 #include <FBaseSysLog.h>
25 #include "FMedia_Ffmpeg.h"
26 #include "FMedia_IVideoEncoder.h"
27 #include "FMedia_Mpeg4Encoder.h"
28
29 using namespace std;
30 using namespace Tizen::Base;
31 using namespace Tizen::Io;
32 using namespace Tizen::Base::Collection;
33
34 namespace Tizen { namespace Media
35 {
36
37 static const int _DEFAULT_QSCALE = 3;
38
39 _IVideoEncoder*
40 _Mpeg4Encoder_CreateInstance(void)
41 {
42         return new (std::nothrow) _Mpeg4Encoder();
43 }
44
45 _Mpeg4Encoder::_Mpeg4Encoder(void)
46 {
47         __pCodecCtx = null;
48         __pCodec = null;
49         __pFrame = null;
50         __firstFrame = true;
51 }
52
53 _Mpeg4Encoder::~_Mpeg4Encoder(void)
54 {
55         if (__pCodecCtx != null)
56         {
57                 avcodec_close(__pCodecCtx);
58                 av_free(__pCodecCtx);
59                 __pCodecCtx = null;
60                 __pCodec = null;
61         }
62         if (__pFrame != null)
63         {
64                 av_free(__pFrame);
65         }
66 }
67
68 result
69 _Mpeg4Encoder::Construct(const Tizen::Base::Collection::HashMap* pOption)
70 {
71         result r = E_SUCCESS;
72         int res = 0;
73         Integer* pKey = null;
74         Integer* pValue = null;
75         int key = -1;
76         int value = -1;
77
78         SysTryReturnResult(NID_MEDIA, __pCodecCtx == null, E_INVALID_STATE, "already constructed");
79
80         av_log_set_level (AV_LOG_QUIET);
81         avcodec_register_all();
82
83         __pCodec = avcodec_find_encoder(AV_CODEC_ID_MPEG4);
84         SysTryCatch(NID_MEDIA, __pCodec != null, r = E_SYSTEM, E_SYSTEM, "[%s] Failed to get avcodec encoder",GetErrorMessage(E_SYSTEM));
85
86         __pCodecCtx = avcodec_alloc_context3(__pCodec);
87         SysTryCatch(NID_MEDIA, __pCodecCtx != null, r = E_SYSTEM, E_SYSTEM,
88                            "[%s] Failed to allocate avcodec context", GetErrorMessage(E_SYSTEM));
89
90         __pCodecCtx->bit_rate = BITRATE_IN_BITS;
91         __pCodecCtx->width = WIDTH;
92         __pCodecCtx->height = HEIGHT;
93         __pCodecCtx->time_base.den = 15;
94         __pCodecCtx->time_base.num = 1;
95         __pCodecCtx->gop_size = GOP;
96         __pCodecCtx->pix_fmt = PIX_FMT_YUV420P;
97         __pCodecCtx->max_b_frames = 0;
98         __pCodecCtx->qmin = MIN_QP;
99         __pCodecCtx->qmax = MAX_QP;
100         __pCodecCtx->mpeg_quant = 1;
101
102         // The initialization values are given in the Hashmap
103         if (pOption != null)
104         {
105         std::unique_ptr<IMapEnumerator> pMapEnum(pOption->GetMapEnumeratorN());
106         SysTryCatch(NID_MEDIA, pMapEnum.get() != null, r = E_SYSTEM, E_SYSTEM, "[E_SYSTEM] Propagating");
107
108         while (pMapEnum.get()->MoveNext() == E_SUCCESS)
109         {
110                 pKey = dynamic_cast<Integer*> (pMapEnum.get()->GetKey());
111                 pValue = dynamic_cast<Integer*> (pMapEnum.get()->GetValue());
112
113                 if (pKey == null || pValue == null)
114                 {
115                         continue;
116                 }
117
118                 key = pKey->ToInt();
119                 value = pValue->ToInt();
120
121                 switch (key)
122                 {
123                 case MEDIA_PROPERTY_VIDEO_WIDTH:
124                         SysTryCatch(NID_MEDIA, value > 0 && value < MAX_WIDTH,
125                                            r = E_INVALID_ARG, E_INVALID_ARG, "[%s] Invalid argument is used: width %d", GetErrorMessage(E_INVALID_ARG), value);
126                         __pCodecCtx->width = value;
127                         break;
128
129                 case MEDIA_PROPERTY_VIDEO_HEIGHT:
130                         SysTryCatch(NID_MEDIA, value > 0 && value < MAX_HEIGHT,
131                                            r = E_INVALID_ARG, E_INVALID_ARG, "[%s] Invalid argument is used: height %d", GetErrorMessage(E_INVALID_ARG), value);
132                         __pCodecCtx->height = value;
133                         break;
134
135                 case MEDIA_PROPERTY_VIDEO_FRAME_RATE:
136                         SysTryCatch(NID_MEDIA, value > 0, r = E_INVALID_ARG, E_INVALID_ARG,
137                                            "[%s] Invalid argument is used: framerate %d", GetErrorMessage(E_INVALID_ARG), value);
138                         __pCodecCtx->time_base.den = value;
139                         __pCodecCtx->time_base.num = 1;
140                         break;
141
142                 case MEDIA_PROPERTY_VIDEO_PIXEL_FORMAT:
143                         SysTryCatch(NID_MEDIA, value == MEDIA_PIXEL_FORMAT_YUV420P, r = E_INVALID_ARG, E_INVALID_ARG,
144                                            "[%s] Invalid argument is used: pixelFormat %d", GetErrorMessage(E_INVALID_ARG), value);
145                         __pCodecCtx->pix_fmt = PIX_FMT_YUV420P;
146                         break;
147
148                 case MEDIA_PROPERTY_VIDEO_BIT_RATE:
149                         SysTryCatch(NID_MEDIA, value >= 0, r = E_INVALID_ARG, E_INVALID_ARG,
150                                            "[%s] Invalid argument is used: bitRate %d", GetErrorMessage(E_INVALID_ARG), value);
151                         __pCodecCtx->bit_rate = value;
152                         break;
153
154                 case MEDIA_PROPERTY_VIDEO_QUANTIZATION_PARAMETER :
155                         SysTryCatch(NID_MEDIA, value >= MIN_QP && value <= MAX_QP,
156                                            r = E_INVALID_ARG, E_INVALID_ARG,
157                                            "[%s] Invalid argument is used: quantization parameter %d", GetErrorMessage(E_INVALID_ARG), value);
158                         //__pCodecCtx->mpeg_quant = value;
159                         break;
160
161                 case MEDIA_PROPERTY_VIDEO_QUANTIZATION_MIN :
162                         SysTryCatch(NID_MEDIA, value >= MIN_QP && value <= MAX_QP,
163                                            r = E_INVALID_ARG, E_INVALID_ARG,
164                                            "[%s] Invalid argument is used: quantization min %d", GetErrorMessage(E_INVALID_ARG), value);
165                         __pCodecCtx->qmin = value;
166                         break;
167
168                 case MEDIA_PROPERTY_VIDEO_QUANTIZATION_MAX :
169                         SysTryCatch(NID_MEDIA, value >= MIN_QP && value <= MAX_QP,
170                                            r = E_INVALID_ARG, E_INVALID_ARG,
171                                            "[%s] Invalid argument is used: quantization max %d", GetErrorMessage(E_INVALID_ARG), value);
172                         __pCodecCtx->qmax = value;
173                         break;
174
175                 case MEDIA_PROPERTY_VIDEO_GOP_SIZE :
176                         SysTryCatch(NID_MEDIA, value  >= 0,
177                                            r = E_INVALID_ARG, E_INVALID_ARG, "[%s] Invalid argument is used: gop size %d", GetErrorMessage(E_INVALID_ARG), value);
178                         __pCodecCtx->gop_size = value;
179                         break;
180
181                 case MEDIA_PROPERTY_VIDEO_PACKET_SIZE  :
182                         SysTryCatch(NID_MEDIA, value >= 0,
183                                            r = E_INVALID_ARG, E_INVALID_ARG, "[%s] Invalid argument is used: slice size %d", GetErrorMessage(E_INVALID_ARG), value);
184                         __pCodecCtx->rtp_payload_size = value;
185                         break;
186
187                 case MEDIA_PROPERTY_VIDEO_USE_AC_PREDICTION :
188                         SysTryCatch(NID_MEDIA, value == 0 || value == 1, r = E_INVALID_ARG, E_INVALID_ARG,
189                                            "[%s] Invalid argument is used: slice size %d", GetErrorMessage(E_INVALID_ARG), value);
190                         break;
191
192                 case MEDIA_PROPERTY_VIDEO_USE_HEADER_EXTENSION_CODE :
193                         SysTryCatch(NID_MEDIA, value == 0 || value == 1, r = E_INVALID_ARG, E_INVALID_ARG,
194                                            "[%s] Invalid argument is used: slice size %d", GetErrorMessage(E_INVALID_ARG), value);
195                         break;
196
197                 case MEDIA_PROPERTY_VIDEO_USE_FRAME_SKIP:
198                         SysTryCatch(NID_MEDIA, value == 0 || value == 1, r = E_INVALID_ARG, E_INVALID_ARG,
199                                            "[%s] Invalid argument is used: slice size %d", GetErrorMessage(E_INVALID_ARG), value);
200                         break;
201
202                 default:
203
204                         break;
205                 }
206         }
207         }
208
209         res = avcodec_open2(__pCodecCtx, __pCodec, NULL);
210
211         SysTryCatch(NID_MEDIA, res >= 0, r = E_SYSTEM, E_SYSTEM,
212                            "[%s] avcodec open failed %d ", GetErrorMessage(E_SYSTEM), res);
213
214         __pFrame = avcodec_alloc_frame();
215         SysTryCatch(NID_MEDIA, __pFrame != null, r = E_SYSTEM, E_SYSTEM, "[%s] avcodec alloc frame failed", GetErrorMessage(E_SYSTEM));
216
217         __pFrame->format = __pCodecCtx->pix_fmt;
218         __pFrame->pts  = 0;
219         for (int i = 0; i < 4; i++)
220         {
221                 __pFrame->data[i] = null;
222                 __pFrame->linesize[i] = 0;
223         }
224         __firstFrame = true;
225
226         return r;
227
228 CATCH:
229
230         if (__pCodecCtx)
231         {
232                 avcodec_close(__pCodecCtx);
233                 av_free(__pCodecCtx);
234                 __pCodecCtx = null;
235                 __pCodec = null;
236         }
237         return r;
238 }
239
240 result
241 _Mpeg4Encoder::Encode(const byte* srcBuf, int& srcBufLength, byte*& dstBuf, int& dstBufLength)
242 {
243         result r = E_SUCCESS;
244         int res = 0;
245         int outIndex = 0;
246         int minSrcBufLength = 0;
247         int gotOutput = 0;
248         unique_ptr<AVPacket, _FfmpegDeleter> pPkt(null, ffmpegDeleter);
249
250         SysTryReturnResult(NID_MEDIA, __pCodecCtx, E_INVALID_STATE, "not constructed");
251
252         minSrcBufLength = __pCodecCtx->width * __pCodecCtx->height * 3 / 2;
253         SysTryCatch(NID_MEDIA, (srcBuf != null && dstBuf != null && srcBufLength >= minSrcBufLength && dstBufLength >= FF_MIN_BUFFER_SIZE),
254                            r = E_OUT_OF_MEMORY, E_OUT_OF_MEMORY,
255                            "[E_INVALID_ARG] Encode Failed : 0x%x %d %d 0x%x %d %d",
256                            srcBuf, srcBufLength, minSrcBufLength, dstBuf, dstBufLength, FF_MIN_BUFFER_SIZE);
257
258         __pFrame->data[0] = (uint8_t*)srcBuf;
259         __pFrame->data[1] = (uint8_t*)srcBuf + __pCodecCtx->width * __pCodecCtx->height;
260         __pFrame->data[2] = (uint8_t*)srcBuf + __pCodecCtx->width * __pCodecCtx->height * 5 / 4;
261         __pFrame->linesize[0] = __pCodecCtx->width;
262         __pFrame->linesize[1] = __pCodecCtx->width / 2;
263         __pFrame->linesize[2] = __pCodecCtx->width / 2;
264
265         if (__firstFrame && __pCodecCtx->extradata)
266         {
267                 SysLog(NID_MEDIA, "extradata:%x %d", __pCodecCtx->extradata, __pCodecCtx->extradata_size);
268                 memcpy(dstBuf, __pCodecCtx->extradata, __pCodecCtx->extradata_size);
269                 __firstFrame = false;
270                 dstBufLength -= __pCodecCtx->extradata_size;
271                 outIndex += __pCodecCtx->extradata_size;
272         }
273
274         pPkt.reset(new (std::nothrow) AVPacket);
275         SysTryReturnResult(NID_MEDIA, pPkt.get() != null, E_OUT_OF_MEMORY, "new AVPacket failed.");
276         av_init_packet(pPkt.get());
277         pPkt->data = NULL;    // packet data will be allocated by the encoder
278         pPkt->size = 0;
279
280         __pFrame->pts++;
281         res = avcodec_encode_video2(__pCodecCtx, pPkt.get(), __pFrame, &gotOutput);
282         SysTryCatch(NID_MEDIA, res >= 0, r = E_SYSTEM, E_SYSTEM, "[%s] Video Encode Failed %d", GetErrorMessage(E_SYSTEM), res);
283
284         if (gotOutput)
285         {
286                 SysTryCatch(NID_MEDIA, pPkt->size <= dstBufLength, r = E_INVALID_ARG, E_INVALID_ARG,
287                                         "[%s] dst buffer is insufficient:%d %d", GetErrorMessage(E_INVALID_ARG), dstBufLength, pPkt->size);
288                 memcpy(dstBuf+outIndex, pPkt->data, pPkt->size);
289                 srcBufLength = minSrcBufLength;
290                 dstBufLength = pPkt->size + outIndex;
291         }
292         else
293         {
294                 srcBufLength = minSrcBufLength;
295                 dstBufLength = outIndex;
296         }
297
298 CATCH:
299         return r;
300 }
301
302
303 result
304 _Mpeg4Encoder::Reset(void)
305 {
306         result r = E_SUCCESS;
307
308         SysTryReturnResult(NID_MEDIA, __pCodecCtx, E_INVALID_STATE, "not constructed");
309         avcodec_flush_buffers(__pCodecCtx);
310         return r;
311 }
312
313 result
314 _Mpeg4Encoder::SetValue(MediaPropertyType type, int value)
315 {
316         result r = E_SUCCESS;
317
318         SysTryReturnResult(NID_MEDIA, __pCodecCtx, E_INVALID_STATE, "not constructed");
319         SysTryCatch(NID_MEDIA, value >= 0, r = E_INVALID_ARG, E_INVALID_ARG,
320                            "[%s] Invalid argument is used. type:%d", GetErrorMessage(E_SYSTEM), type);
321         SysTryCatch(NID_MEDIA, type > 0, r = E_INVALID_ARG, E_INVALID_ARG,
322                            "[%s] Invalid argument is used. type:%d", GetErrorMessage(E_SYSTEM), type);
323         SysTryCatch(NID_MEDIA, type >= MEDIA_PROPERTY_VIDEO_WIDTH && type <= MEDIA_PROPERTY_AUDIO_SAMPLE_RATE,
324                            r = E_OBJ_NOT_FOUND, E_OBJ_NOT_FOUND, "[%s] object not found: type:%d", GetErrorMessage(E_OBJ_NOT_FOUND),type);
325
326         switch (type)
327         {
328         case MEDIA_PROPERTY_VIDEO_WIDTH:
329         SysTryCatch(NID_MEDIA, value < MAX_WIDTH, r = E_INVALID_ARG, E_INVALID_ARG,
330                            "[%s] Invalid argument is used. type:%d", GetErrorMessage(E_INVALID_ARG), type);
331                 __pCodecCtx->width = value;
332         break;
333
334         case MEDIA_PROPERTY_VIDEO_HEIGHT:
335         SysTryCatch(NID_MEDIA, value < MAX_HEIGHT, r = E_INVALID_ARG, E_INVALID_ARG,
336                            "[%s] Invalid argument is used. type:%d", GetErrorMessage(E_INVALID_ARG), type);
337                 __pCodecCtx->height = value;
338         break;
339
340         case MEDIA_PROPERTY_VIDEO_PIXEL_FORMAT:
341                 __pCodecCtx->pix_fmt = PIX_FMT_YUV420P;
342                 break;
343
344         case MEDIA_PROPERTY_VIDEO_FRAME_RATE:
345                 __pCodecCtx->time_base.den = 15;
346                 __pCodecCtx->time_base.num = 1;
347                 break;
348
349         case MEDIA_PROPERTY_VIDEO_BIT_RATE:
350                 __pCodecCtx->bit_rate = value;
351                 break;
352
353
354         case MEDIA_PROPERTY_VIDEO_QUANTIZATION_PARAMETER :
355                 __pCodecCtx->mpeg_quant = value;
356         break;
357
358         case MEDIA_PROPERTY_VIDEO_QUANTIZATION_MIN :
359                 __pCodecCtx->qmin = value;
360                 break;
361
362         case MEDIA_PROPERTY_VIDEO_QUANTIZATION_MAX :
363                 __pCodecCtx->qmax = value;
364                 break;
365
366         case MEDIA_PROPERTY_VIDEO_GOP_SIZE :
367                 __pCodecCtx->gop_size = value;
368                 break;
369
370         case MEDIA_PROPERTY_VIDEO_PACKET_SIZE  :
371                 __pCodecCtx->rtp_payload_size = value;
372         break;
373
374         default:
375                 return E_OBJ_NOT_FOUND;
376         break;
377         }
378         return r;
379
380 CATCH:
381         return r;
382 }
383
384 result
385 _Mpeg4Encoder::SetValue(MediaPropertyType type, bool value)
386 {
387         result r = E_SUCCESS;
388
389         SysTryReturnResult(NID_MEDIA, __pCodecCtx, E_INVALID_STATE, "not constructed");
390         SysTryCatch(NID_MEDIA, type > 0, r = E_INVALID_ARG, E_INVALID_ARG, "[%s] Invalid argument is used", GetErrorMessage(E_INVALID_ARG));
391         SysTryCatch(NID_MEDIA,(value ==0 || value == 1), r = E_INVALID_ARG, E_INVALID_ARG, "[%s] Invalid argument is used", GetErrorMessage(E_INVALID_ARG));
392         SysTryCatch(NID_MEDIA, type >= MEDIA_PROPERTY_VIDEO_WIDTH || type <= MEDIA_PROPERTY_AUDIO_SAMPLE_RATE,
393                            r = E_OBJ_NOT_FOUND, E_OBJ_NOT_FOUND, "[E_OBJ_NOT_FOUND] Object Not Found",GetErrorMessage(E_OBJ_NOT_FOUND));
394         switch (type)
395         {
396         case MEDIA_PROPERTY_VIDEO_FORCE_SKIP_FRAME:
397                 __pCodecCtx->skip_frame = AVDISCARD_DEFAULT;
398         break;
399
400         case MEDIA_PROPERTY_VIDEO_FORCE_INTRA_CODING:
401                 __pCodecCtx->skip_idct = AVDISCARD_DEFAULT;
402         break;
403
404         default:
405                 return E_OBJ_NOT_FOUND;
406         break;
407         }
408
409
410         return r;
411
412 CATCH:
413         return r;
414 }
415
416 Tizen::Base::Collection::IListT<MediaPropertyType>*
417 _Mpeg4Encoder::GetSupportedPropertyListN(void) const
418 {
419         result r = E_SUCCESS;
420         ArrayListT<MediaPropertyType>* pPropList = new (std::nothrow) ArrayListT<MediaPropertyType>;
421
422         SysTryCatch(NID_MEDIA, pPropList != null, r = E_OUT_OF_MEMORY, E_OUT_OF_MEMORY,
423                            "[%s] Memory allocation Failed",GetErrorMessage(E_OUT_OF_MEMORY));
424         pPropList->Add(MEDIA_PROPERTY_VIDEO_WIDTH);
425         pPropList->Add(MEDIA_PROPERTY_VIDEO_HEIGHT);
426         pPropList->Add(MEDIA_PROPERTY_VIDEO_PIXEL_FORMAT);
427         pPropList->Add(MEDIA_PROPERTY_VIDEO_FRAME_RATE);
428         pPropList->Add(MEDIA_PROPERTY_VIDEO_BIT_RATE);
429         pPropList->Add(MEDIA_PROPERTY_VIDEO_QUANTIZATION_PARAMETER);
430         pPropList->Add( MEDIA_PROPERTY_VIDEO_QUANTIZATION_MAX);
431         pPropList->Add(MEDIA_PROPERTY_VIDEO_QUANTIZATION_MIN);
432         pPropList->Add(MEDIA_PROPERTY_VIDEO_GOP_SIZE);
433         pPropList->Add(MEDIA_PROPERTY_VIDEO_PACKET_SIZE);
434         pPropList->Add(MEDIA_PROPERTY_VIDEO_USE_FRAME_SKIP);
435         pPropList->Add(MEDIA_PROPERTY_VIDEO_USE_AC_PREDICTION);
436         pPropList->Add(MEDIA_PROPERTY_VIDEO_USE_HEADER_EXTENSION_CODE);
437         pPropList->Add(MEDIA_PROPERTY_VIDEO_FORCE_SKIP_FRAME);
438         pPropList->Add(MEDIA_PROPERTY_VIDEO_FORCE_INTRA_CODING);
439
440         SetLastResult(r);
441         return pPropList;
442
443 CATCH:
444         return null;
445 }
446
447 bool
448 _Mpeg4Encoder::IsPropertySupported(MediaPropertyType type) const
449 {
450         result r = E_SUCCESS;
451
452         SysTryReturnResult(NID_MEDIA, __pCodecCtx, E_INVALID_STATE, "not constructed");
453         SysTryCatch(NID_MEDIA, ( type == MEDIA_PROPERTY_VIDEO_WIDTH || type == MEDIA_PROPERTY_VIDEO_HEIGHT
454                                                         || type == MEDIA_PROPERTY_VIDEO_PIXEL_FORMAT || type == MEDIA_PROPERTY_VIDEO_FRAME_RATE
455                         || type == MEDIA_PROPERTY_VIDEO_BIT_RATE || type == MEDIA_PROPERTY_VIDEO_QUANTIZATION_PARAMETER || type ==
456                         MEDIA_PROPERTY_VIDEO_QUANTIZATION_MIN || type == MEDIA_PROPERTY_VIDEO_QUANTIZATION_MAX || type == MEDIA_PROPERTY_VIDEO_GOP_SIZE || type ==
457                         MEDIA_PROPERTY_VIDEO_PACKET_SIZE || type ==  MEDIA_PROPERTY_VIDEO_USE_FRAME_SKIP || type ==  MEDIA_PROPERTY_VIDEO_USE_AC_PREDICTION || type ==
458                         MEDIA_PROPERTY_VIDEO_USE_HEADER_EXTENSION_CODE || type ==  MEDIA_PROPERTY_VIDEO_FORCE_SKIP_FRAME || type ==  MEDIA_PROPERTY_VIDEO_FORCE_INTRA_CODING),
459                            r = E_OBJ_NOT_FOUND, E_OBJ_NOT_FOUND,
460                            "[E_OBJ_NOT_FOUND] Object not found. type:%d", type);
461         SetLastResult(r);
462         return true;
463
464 CATCH:
465         return false;
466 }
467
468 }} // Tizen::Media