MythTV  master
filter_kerneldeint.c
Go to the documentation of this file.
1 /* Rewrite of neuron2's KernelDeint filter for Avisynth */
2 
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <unistd.h>
6 
7 #include "mythconfig.h"
8 #if HAVE_STDINT_H
9 #include <stdint.h>
10 #endif
11 
12 #include <string.h>
13 #include <math.h>
14 #include <pthread.h>
15 
16 #include "filter.h"
17 #include "mythframe.h"
18 
19 #include "mythlogging.h"
20 
21 #include "../mm_arch.h"
22 
23 #undef ABS
24 #define ABS(A) ( (A) > 0 ? (A) : -(A) )
25 #define CLAMP(A,L,U) ((A)>(U)?(U):((A)<(L)?(L):(A)))
26 
27 #if HAVE_MMX
28 #include "ffmpeg-mmx.h"
29 #define THRESHOLD 12
30 static const mmx_t mm_lthr = { .w={ -THRESHOLD, -THRESHOLD,
31  -THRESHOLD, -THRESHOLD} };
32 static const mmx_t mm_hthr = { .w={ THRESHOLD - 1, THRESHOLD - 1,
33  THRESHOLD - 1, THRESHOLD - 1} };
34 static const mmx_t mm_cpool[] = { { 0x0000000000000000LL }, };
35 #else
36 #define mmx_t int
37 #endif
38 
40 {
41  int ready;
42  pthread_t id;
43  int exists;
44 };
45 
46 typedef struct ThisFilter
47 {
49 
52  int field;
53  int ready;
57  pthread_mutex_t mutex;
58 
60  int mm_flags;
61  int width;
62  int height;
63  long long last_framenr;
64  uint8_t *ref[3];
65  int ref_stride[3];
66 
70  void (*line_filter)(uint8_t *dst, int width, int start_width,
71  const uint8_t *src1, const uint8_t *src2, const uint8_t *src3,
72  const uint8_t *src4, const uint8_t *src5);
73  void (*line_filter_fast)(uint8_t *dst, int width, int start_width,
74  uint8_t *src1, const uint8_t *src2, const uint8_t *src3,
75  const uint8_t *src4, const uint8_t *src5);
76  TF_STRUCT;
77 } ThisFilter;
78 
79 static void line_filter_c_fast(uint8_t *dst, int width, int start_width,
80  uint8_t *buf, const uint8_t *src2, const uint8_t *src3,
81  const uint8_t *src4, const uint8_t *src5)
82 {
83  for (int X = start_width; X < width; X++)
84  {
85  uint8_t tmp = buf[X];
86  buf[X] = src3[X];
87  if (ABS((int)src3[X] - (int)src2[X]) > 11)
88  dst[X] = CLAMP((src2[X] * 4 + src4[X] * 4
89  + src3[X] * 2 - tmp - src5[X])
90  / 8, 0, 255);
91  }
92 }
93 
94 static void line_filter_c(uint8_t *dst, int width, int start_width,
95  const uint8_t *src1, const uint8_t *src2, const uint8_t *src3,
96  const uint8_t *src4, const uint8_t *src5)
97 {
98  int X;
99  for (X = start_width; X < width; X++)
100  {
101  if (ABS((int)src3[X] - (int)src2[X]) > 11)
102  dst[X] = CLAMP((src2[X] * 4 + src4[X] * 4
103  + src3[X] * 2 - src1[X] - src5[X])
104  / 8, 0, 255);
105  else
106  dst[X] = src3[X];
107  }
108 }
109 
110 #if HAVE_MMX
111 static inline void mmx_start(const uint8_t *src1, const uint8_t *src2,
112  const uint8_t *src3, const uint8_t *src4,
113  int X)
114 {
115  movq_m2r (src2[X], mm0);
116  movq_m2r (src2[X], mm1);
117  movq_m2r (src4[X], mm2);
118  movq_m2r (src4[X], mm3);
119  movq_m2r (src3[X], mm4);
120  movq_m2r (src3[X], mm5);
121  punpcklbw_m2r (mm_cpool[0], mm0);
122  punpckhbw_m2r (mm_cpool[0], mm1);
123  punpcklbw_m2r (mm_cpool[0], mm2);
124  punpckhbw_m2r (mm_cpool[0], mm3);
125  movq_r2r (mm0, mm6);
126  movq_r2r (mm1, mm7);
127  paddw_r2r (mm2, mm0);
128  paddw_r2r (mm3, mm1);
129  movq_m2r (src3[X], mm2);
130  movq_m2r (src3[X], mm3);
131  psllw_i2r (2, mm0);
132  psllw_i2r (2, mm1);
133  punpcklbw_m2r (mm_cpool[0], mm2);
134  punpckhbw_m2r (mm_cpool[0], mm3);
135  psllw_i2r (1, mm2);
136  psllw_i2r (1, mm3);
137  paddw_r2r (mm2, mm0);
138  paddw_r2r (mm3, mm1);
139  movq_m2r (src1[X], mm2);
140  movq_m2r (src1[X], mm3);
141  punpcklbw_m2r (mm_cpool[0], mm2);
142  punpckhbw_m2r (mm_cpool[0], mm3);
143 }
144 
145 static inline void mmx_end(const uint8_t *src3, const uint8_t *src5,
146  uint8_t *dst, int X)
147 {
148  punpcklbw_m2r (mm_cpool[0], mm4);
149  punpckhbw_m2r (mm_cpool[0], mm5);
150  psubusw_r2r (mm2, mm0);
151  psubusw_r2r (mm3, mm1);
152  movq_m2r (src5[X], mm2);
153  movq_m2r (src5[X], mm3);
154  punpcklbw_m2r (mm_cpool[0], mm2);
155  punpckhbw_m2r (mm_cpool[0], mm3);
156  psubusw_r2r (mm2, mm0);
157  psubusw_r2r (mm3, mm1);
158  psrlw_i2r (3, mm0);
159  psrlw_i2r (3, mm1);
160  psubw_r2r (mm6, mm4);
161  psubw_r2r (mm7, mm5);
162  packuswb_r2r (mm1,mm0);
163  movq_r2r (mm4, mm6);
164  movq_r2r (mm5, mm7);
165  pcmpgtw_m2r (mm_lthr, mm4);
166  pcmpgtw_m2r (mm_lthr, mm5);
167  pcmpgtw_m2r (mm_hthr, mm6);
168  pcmpgtw_m2r (mm_hthr, mm7);
169  packsswb_r2r (mm5, mm4);
170  packsswb_r2r (mm7, mm6);
171  pxor_r2r (mm6, mm4);
172  movq_r2r (mm4, mm5);
173  pandn_r2r (mm0, mm4);
174  pand_m2r (src3[X], mm5);
175  por_r2r (mm4, mm5);
176  movq_r2m (mm5, dst[X]);
177 }
178 
179 static void line_filter_mmx_fast(uint8_t *dst, int width, int start_width,
180  uint8_t *buf, const uint8_t *src2, const uint8_t *src3,
181  const uint8_t *src4, const uint8_t *src5)
182 {
183  int X;
184  for (X = start_width; X < width - 7; X += 8)
185  {
186  mmx_start(buf, src2, src3, src4, X);
187  movq_r2m (mm4, buf[X]);
188  mmx_end(src3, src5, dst, X);
189  }
190 
191  line_filter_c_fast(dst, width, X, buf, src2, src3, src4, src5);
192 }
193 
194 static void line_filter_mmx(uint8_t *dst, int width, int start_width,
195  const uint8_t *src1, const uint8_t *src2, const uint8_t *src3,
196  const uint8_t *src4, const uint8_t *src5)
197 {
198  int X;
199  for (X = start_width; X < width - 7; X += 8)
200  {
201  mmx_start(src1, src2, src3, src4, X);
202  mmx_end(src3, src5, dst, X);
203  }
204 
205  line_filter_c(dst, width, X, src1, src2, src3, src4, src5);
206 }
207 #endif
208 
209 static void store_ref(struct ThisFilter *p, uint8_t *src, int src_offsets[3],
210  int src_stride[3], int width, int height)
211 {
212  int i;
213  for (i = 0; i < 3; i++)
214  {
215  if (src_stride[i] < 1)
216  continue;
217 
218  int is_chroma = !!i;
219  int h = height >> is_chroma;
220  int w = width >> is_chroma;
221 
222  if (p->ref_stride[i] == src_stride[i])
223  {
224  memcpy(p->ref[i], src + src_offsets[i], src_stride[i] * h);
225  }
226  else
227  {
228  int j;
229  uint8_t *src2 = src + src_offsets[i];
230  uint8_t *dest = p->ref[i];
231  for (j = 0; j < h; j++)
232  {
233  memcpy(dest, src2, w);
234  src2 += src_stride[i];
235  dest += p->ref_stride[i];
236  }
237  }
238  }
239 }
240 
241 static int AllocFilter(ThisFilter* filter, int width, int height)
242 {
243  if ((width != filter->width) || (height != filter->height))
244  {
245  int i;
246  for (i = 0; i < 3; i++)
247  {
248  if (filter->ref[i])
249  free(filter->ref[i]);
250 
251  int is_chroma= !!i;
252  int w = ((width + 31) & (~31)) >> is_chroma;
253  int h = ((height + 6 + 31) & (~31)) >> is_chroma;
254  int size = w * h * sizeof(uint8_t);
255 
256  filter->ref_stride[i] = w;
257  filter->ref[i] = (uint8_t*) malloc(size);
258  if (!filter->ref[i])
259  return 0;
260  memset(filter->ref[i], is_chroma ? 127 : 0, size);
261  }
262  filter->width = width;
263  filter->height = height;
264  }
265  return 1;
266 }
267 
268 static void filter_func(struct ThisFilter *p, uint8_t *dst, int dst_offsets[3],
269  int dst_stride[3], int width, int height, int parity,
270  int tff, int double_rate, int dirty,
271  int this_slice, int total_slices)
272 {
273  if (height < 8 || total_slices < 1)
274  return;
275 
276  if (total_slices > 1 && !double_rate)
277  {
278  this_slice = 0;
279  total_slices = 1;
280  }
281 
282  int i, y;
283  uint8_t *dest, *src1, *src2, *src3, *src4, *src5;
284  int channels = p->skipchroma ? 1 : 3;
285  int field = parity ^ tff;
286 
287  int first_slice = (this_slice == 0);
288  int last_slice = 0;
289  int slice_height = height / total_slices;
290  slice_height = (slice_height >> 1) << 1;
291  int starth = slice_height * this_slice;
292  int endh = starth + slice_height;
293 
294  if ((this_slice + 1) >= total_slices)
295  {
296  endh = height;
297  last_slice = 1;
298  }
299 
300  for (i = 0; i < channels; i++)
301  {
302 
303  int is_chroma = !!i;
304  int w = width >> is_chroma;
305  int start = starth >> is_chroma;
306  int end = endh >> is_chroma;
307 
308  if (!first_slice)
309  start -= 2;
310  if (last_slice)
311  end -= (5 + field);
312 
313  int src_pitch = p->ref_stride[i];
314  dest = dst + dst_offsets[i] + (start * dst_stride[i]);
315  src1 = p->ref[i] + (start * src_pitch);
316 
317  if (double_rate)
318  {
319  src2 = src1 + src_pitch;
320  src3 = src2 + src_pitch;
321  src4 = src3 + src_pitch;
322  src5 = src4 + src_pitch;
323 
324  if (first_slice)
325  {
326  if (!field)
327  p->line_filter(dest, w, 0, src1, src1, src1, src2, src3);
328  else if (dirty)
329  memcpy(dest, src1, w);
330  dest += dst_stride[i];
331 
332  if (field)
333  p->line_filter(dest, w, 0, src1, src1, src2, src3, src4);
334  else if (dirty)
335  memcpy(dest, src2, w);
336  dest += dst_stride[i];
337  }
338  else
339  {
340  dest += dst_stride[i] << 1;
341  }
342 
343  for (y = start; y < end; y++)
344  {
345  if ((y ^ (1 - field)) & 1)
346  p->line_filter(dest, w, 0, src1, src2, src3, src4, src5);
347  else if (dirty)
348  memcpy(dest, src3, w);
349 
350  dest += dst_stride[i];
351  src1 = src2;
352  src2 = src3;
353  src3 = src4;
354  src4 = src5;
355  src5 += src_pitch;
356  }
357 
358  if (last_slice)
359  {
360  if (!field)
361  p->line_filter(dest, w, 0, src2, src3, src4, src5, src5);
362  else if (dirty)
363  memcpy(dest, src4, w);
364  dest += dst_stride[i];
365 
366  if (field)
367  p->line_filter(dest, w, 0, src3, src4, src5, src5, src5);
368  else if (dirty)
369  memcpy(dest, src5, w);
370  }
371  }
372  else
373  {
374  int field_stride = dst_stride[i] << 1;
375  src2 = dest + dst_stride[i];
376  src3 = src2 + dst_stride[i];
377  src4 = src3 + dst_stride[i];
378  src5 = src4 + dst_stride[i];
379  memcpy(src1, dest, w);
380 
381  if (field)
382  {
383  dest += dst_stride[i];
384  p->line_filter_fast(dest, w, 0, src1, src2, src2, src3, src4);
385  src2 = src3;
386  src3 = src4;
387  src4 = src5;
388  src5 += dst_stride[i];
389  }
390  else
391  {
392  p->line_filter_fast(dest, w, 0, src1, src2, src2, src2, src3);
393  }
394  dest += field_stride;
395 
396  for (y = start; y < end; y += 2)
397  {
398  p->line_filter_fast(dest, w, 0, src1, src2, src3, src4, src5);
399  dest += field_stride;
400  src2 = src4;
401  src3 = src5;
402  src4 += field_stride;
403  src5 += field_stride;
404  }
405 
406  if (field)
407  p->line_filter_fast(dest, w, 0, src1, src4, src5, src5, src5);
408  else
409  p->line_filter_fast(dest, w, 0, src1, src3, src4, src5, src5);
410  }
411  }
412 #if HAVE_MMX
413  if (p->mm_flags & AV_CPU_FLAG_MMX)
414  emms();
415 #endif
416 }
417 
418 static void *KernelThread(void *args)
419 {
420  ThisFilter *filter = (ThisFilter*)args;
421 
422  pthread_mutex_lock(&(filter->mutex));
423  int num = filter->actual_threads;
424  filter->actual_threads = num + 1;
425  pthread_mutex_unlock(&(filter->mutex));
426 
427  while (!filter->kill_threads)
428  {
429  usleep(1000);
430  if (filter->ready &&
431  filter->frame != NULL &&
432  filter->threads[num].ready)
433  {
434  filter_func(
435  filter, filter->frame->buf, filter->frame->offsets,
436  filter->frame->pitches, filter->frame->width,
437  filter->frame->height, filter->field,
438  filter->frame->top_field_first, filter->double_rate,
439  filter->dirty_frame, num, filter->actual_threads);
440 
441  pthread_mutex_lock(&(filter->mutex));
442  filter->ready = filter->ready - 1;
443  filter->threads[num].ready = 0;
444  pthread_mutex_unlock(&(filter->mutex));
445  }
446  }
447  pthread_exit(NULL);
448  return NULL;
449 }
450 
451 static int KernelDeint(VideoFilter *f, VideoFrame *frame, int field)
452 {
453  ThisFilter *filter = (ThisFilter *) f;
454  TF_VARS;
455 
456  if (!AllocFilter(filter, frame->width, frame->height))
457  {
458  LOG(VB_GENERAL, LOG_ERR, "KernelDeint: failed to allocate buffers.");
459  return -1;
460  }
461 
462  TF_START;
463 
464  filter->dirty_frame = 1;
465  if (filter->last_framenr == frame->frameNumber)
466  {
467  filter->double_call = 1;
468  }
469  else
470  {
471  filter->double_rate = filter->double_call;
472  filter->double_call = 0;
473  filter->dirty_frame = 0;
474  if (filter->double_rate)
475  {
476  store_ref(filter, frame->buf, frame->offsets,
477  frame->pitches, frame->width, frame->height);
478  }
479  }
480 
481  if (filter->actual_threads > 1 && filter->double_rate)
482  {
483  int i;
484  for (i = 0; i < filter->actual_threads; i++)
485  filter->threads[i].ready = 1;
486  filter->frame = frame;
487  filter->field = field;
488  filter->ready = filter->actual_threads;
489  i = 0;
490  while (filter->ready > 0 && i < 1000)
491  {
492  usleep(1000);
493  i++;
494  }
495  }
496  else
497  {
498  filter_func(
499  filter, frame->buf, frame->offsets, frame->pitches,
500  frame->width, frame->height, field, frame->top_field_first,
501  filter->double_rate, filter->dirty_frame, 0, 1);
502  }
503 
504  filter->last_framenr = frame->frameNumber;
505 
506  TF_END(filter, "KernelDeint: ");
507 
508  return 0;
509 }
510 
512 {
513  ThisFilter *filter = (ThisFilter *) f;
514 
515  int i;
516  for (i = 0; i < 3; i++)
517  {
518  uint8_t **p= &filter->ref[i];
519  if (*p)
520  free(*p);
521  *p= NULL;
522  }
523 
524  if (filter->threads != NULL)
525  {
526  filter->kill_threads = 1;
527  for (i = 0; i < filter->requested_threads; i++)
528  if (filter->threads[i].exists)
529  pthread_join(filter->threads[i].id, NULL);
530  free(filter->threads);
531  }
532 }
533 
535  VideoFrameType outpixfmt,
536  const int *width, const int *height,
537  const char *options, int threads)
538 {
539  ThisFilter *filter;
540  (void) options;
541  (void) threads;
542 
543  if (inpixfmt != FMT_YV12 || outpixfmt != FMT_YV12)
544  {
545  LOG(VB_GENERAL, LOG_ERR, "KernelDeint: valid formats are YV12->YV12");
546  return NULL;
547  }
548 
549  filter = (ThisFilter *) malloc (sizeof(ThisFilter));
550  if (filter == NULL)
551  {
552  LOG(VB_GENERAL, LOG_ERR,
553  "KernelDeint: failed to allocate memory for filter.");
554  return NULL;
555  }
556 
557  filter->mm_flags = 0;
558  filter->line_filter = &line_filter_c;
560 #if HAVE_MMX
561  filter->mm_flags = av_get_cpu_flags();
562  if (filter->mm_flags & AV_CPU_FLAG_MMX)
563  {
564  filter->line_filter = &line_filter_mmx;
565  filter->line_filter_fast = &line_filter_mmx_fast;
566  }
567 #endif
568 
569  filter->skipchroma = 0;
570  filter->width = 0;
571  filter->height = 0;
572  filter->last_framenr = -1;
573  filter->double_call = 0;
574  filter->double_rate = 1;
575  memset(filter->ref, 0, sizeof(filter->ref));
576  if (!AllocFilter(filter, *width, *height))
577  {
578  LOG(VB_GENERAL, LOG_ERR, "KernelDeint: failed to allocate buffers.");
579  free (filter);
580  return NULL;
581  }
582 
583  TF_INIT(filter);
584 
585  filter->vf.filter = &KernelDeint;
587 
588  filter->frame = NULL;
589  filter->field = 0;
590  filter->ready = 0;
591  filter->kill_threads = 0;
592  filter->actual_threads = 0;
593  filter->requested_threads = threads;
594  filter->threads = NULL;
595 
596  if (filter->requested_threads > 1)
597  {
598  filter->threads = (struct DeintThread *) calloc(threads,
599  sizeof(struct DeintThread));
600  if (filter->threads == NULL)
601  {
602  LOG(VB_GENERAL, LOG_ERR, "KernelDeint: failed to allocate memory "
603  "for threads - falling back to existing, single thread.");
604  filter->requested_threads = 1;
605  }
606  }
607 
608  if (filter->requested_threads > 1)
609  {
610  pthread_mutex_init(&(filter->mutex), NULL);
611  int success = 0;
612  for (int i = 0; i < filter->requested_threads; i++)
613  {
614  if (pthread_create(&(filter->threads[i].id), NULL,
615  KernelThread, (void*)filter) != 0)
616  filter->threads[i].exists = 0;
617  else
618  {
619  success++;
620  filter->threads[i].exists = 1;
621  }
622  }
623 
624  if (success < filter->requested_threads)
625  {
626  LOG(VB_GENERAL, LOG_NOTICE,
627  "KernelDeint: failed to create all threads - "
628  "falling back to existing, single thread.");
629  }
630  else
631  {
632  int timeout = 0;
633  while (filter->actual_threads != filter->requested_threads)
634  {
635  timeout++;
636  if (timeout > 5000)
637  {
638  LOG(VB_GENERAL, LOG_NOTICE,
639  "KernelDeint: waited too long for "
640  "threads to start.- continuing.");
641  break;
642  }
643  usleep(1000);
644  }
645  LOG(VB_PLAYBACK, LOG_INFO, "KernelDeint: Created threads.");
646  }
647  }
648 
649  if (filter->actual_threads < 1 )
650  LOG(VB_PLAYBACK, LOG_INFO, "KernelDeint: Using existing thread.");
651 
652  return (VideoFilter *) filter;
653 }
654 
655 static FmtConv FmtList[] =
656 {
657  { FMT_YV12, FMT_YV12 },
658  FMT_NULL
659 };
660 
662 {
663  {
665  .name= (char*)"kerneldeint",
666  .descript= (char*)"combines data from several fields to deinterlace "
667  "with less motion blur",
668  .formats= FmtList,
669  .libname= NULL
670  },
671  {
672  .filter_init= &NewKernelDeintFilter,
673  .name= (char*)"kerneldoubleprocessdeint",
674  .descript= (char*)"combines data from several fields to deinterlace "
675  "with less motion blur",
676  .formats= FmtList,
677  .libname= NULL
678  },
679  FILT_NULL
680 };
int pitches[3]
Y, U, & V pitches.
Definition: mythframe.h:63
int(* filter)(struct VideoFilter_ *, VideoFrame *, int)
Definition: filter.h:37
static void line_filter_c(uint8_t *dst, int width, int start_width, const uint8_t *src1, const uint8_t *src2, const uint8_t *src3, const uint8_t *src4, const uint8_t *src5)
init_filter filter_init
Definition: filter.h:28
struct DeintThread * threads
static VideoFilter * NewKernelDeintFilter(VideoFrameType inpixfmt, VideoFrameType outpixfmt, const int *width, const int *height, const char *options, int threads)
unsigned char * dst[3]
#define TF_VARS
Definition: filter.h:112
#define NULL
Definition: H264Parser.h:62
static const mmx_t mm_cpool[]
pthread_mutex_t mutex
void(* cleanup)(struct VideoFilter_ *)
Definition: filter.h:38
enum FrameType_ VideoFrameType
static int KernelDeint(VideoFilter *f, VideoFrame *frame, int field)
static guint32 * tmp
Definition: goom_core.c:35
int offsets[3]
Y, U, & V offsets.
Definition: mythframe.h:64
VideoFilter vf
Definition: filter_adjust.c:36
#define ABS(A)
static void line_filter_c_fast(uint8_t *dst, int width, int start_width, uint8_t *buf, const uint8_t *src2, const uint8_t *src3, const uint8_t *src4, const uint8_t *src5)
static void CleanupKernelDeintFilter(VideoFilter *f)
static FmtConv FmtList[]
int height
Definition: mythframe.h:42
const FilterInfo filter_table[]
long long last_framenr
static void * KernelThread(void *args)
long long frameNumber
Definition: mythframe.h:48
int top_field_first
1 if top field is first.
Definition: mythframe.h:58
struct ThisFilter ThisFilter
void(* line_filter_fast)(uint8_t *dst, int width, int start_width, uint8_t *src1, const uint8_t *src2, const uint8_t *src3, const uint8_t *src4, const uint8_t *src5)
#define FMT_NULL
Definition: filter.h:20
#define mmx_t
#define TF_END(filter, prefix)
Definition: filter.h:114
static int AllocFilter(ThisFilter *filter, int width, int height)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
#define emms()
Definition: mm_arch.h:15
static void store_ref(struct ThisFilter *p, uint8_t *src, int src_offsets[3], int src_stride[3], int width, int height)
static void filter_func(struct ThisFilter *p, uint8_t *dst, int dst_offsets[3], int dst_stride[3], int width, int height, int parity, int tff, int double_rate, int dirty, int this_slice, int total_slices)
uint8_t * ref[NREFS+1][NCHANS]
#define FILT_NULL
Definition: filter.h:47
VideoFrame * frame
#define CLAMP(A, L, U)
#define TF_INIT(filter)
Definition: filter.h:110
#define TF_START
Definition: filter.h:113
unsigned char * buf
Definition: mythframe.h:39
void(* line_filter)(uint8_t *dst, int width, int start_width, const uint8_t *src1, const uint8_t *src2, const uint8_t *src3, const uint8_t *src4, const uint8_t *src5)