FFmpeg  4.3
vp9mvs.c
Go to the documentation of this file.
1 /*
2  * VP9 compatible video decoder
3  *
4  * Copyright (C) 2013 Ronald S. Bultje <rsbultje gmail com>
5  * Copyright (C) 2013 Clément Bœsch <u pkh me>
6  *
7  * This file is part of FFmpeg.
8  *
9  * FFmpeg is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * FFmpeg is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with FFmpeg; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  */
23 
24 #include "internal.h"
25 #include "vp56.h"
26 #include "vp9.h"
27 #include "vp9data.h"
28 #include "vp9dec.h"
29 
30 static av_always_inline void clamp_mv(VP56mv *dst, const VP56mv *src,
31  VP9TileData *td)
32 {
33  dst->x = av_clip(src->x, td->min_mv.x, td->max_mv.x);
34  dst->y = av_clip(src->y, td->min_mv.y, td->max_mv.y);
35 }
36 
38  VP56mv *pmv, int ref, int z, int idx, int sb)
39 {
40  static const int8_t mv_ref_blk_off[N_BS_SIZES][8][2] = {
41  [BS_64x64] = { { 3, -1 }, { -1, 3 }, { 4, -1 }, { -1, 4 },
42  { -1, -1 }, { 0, -1 }, { -1, 0 }, { 6, -1 } },
43  [BS_64x32] = { { 0, -1 }, { -1, 0 }, { 4, -1 }, { -1, 2 },
44  { -1, -1 }, { 0, -3 }, { -3, 0 }, { 2, -1 } },
45  [BS_32x64] = { { -1, 0 }, { 0, -1 }, { -1, 4 }, { 2, -1 },
46  { -1, -1 }, { -3, 0 }, { 0, -3 }, { -1, 2 } },
47  [BS_32x32] = { { 1, -1 }, { -1, 1 }, { 2, -1 }, { -1, 2 },
48  { -1, -1 }, { 0, -3 }, { -3, 0 }, { -3, -3 } },
49  [BS_32x16] = { { 0, -1 }, { -1, 0 }, { 2, -1 }, { -1, -1 },
50  { -1, 1 }, { 0, -3 }, { -3, 0 }, { -3, -3 } },
51  [BS_16x32] = { { -1, 0 }, { 0, -1 }, { -1, 2 }, { -1, -1 },
52  { 1, -1 }, { -3, 0 }, { 0, -3 }, { -3, -3 } },
53  [BS_16x16] = { { 0, -1 }, { -1, 0 }, { 1, -1 }, { -1, 1 },
54  { -1, -1 }, { 0, -3 }, { -3, 0 }, { -3, -3 } },
55  [BS_16x8] = { { 0, -1 }, { -1, 0 }, { 1, -1 }, { -1, -1 },
56  { 0, -2 }, { -2, 0 }, { -2, -1 }, { -1, -2 } },
57  [BS_8x16] = { { -1, 0 }, { 0, -1 }, { -1, 1 }, { -1, -1 },
58  { -2, 0 }, { 0, -2 }, { -1, -2 }, { -2, -1 } },
59  [BS_8x8] = { { 0, -1 }, { -1, 0 }, { -1, -1 }, { 0, -2 },
60  { -2, 0 }, { -1, -2 }, { -2, -1 }, { -2, -2 } },
61  [BS_8x4] = { { 0, -1 }, { -1, 0 }, { -1, -1 }, { 0, -2 },
62  { -2, 0 }, { -1, -2 }, { -2, -1 }, { -2, -2 } },
63  [BS_4x8] = { { 0, -1 }, { -1, 0 }, { -1, -1 }, { 0, -2 },
64  { -2, 0 }, { -1, -2 }, { -2, -1 }, { -2, -2 } },
65  [BS_4x4] = { { 0, -1 }, { -1, 0 }, { -1, -1 }, { 0, -2 },
66  { -2, 0 }, { -1, -2 }, { -2, -1 }, { -2, -2 } },
67  };
68  VP9Context *s = td->s;
69  VP9Block *b = td->b;
70  int row = td->row, col = td->col, row7 = td->row7;
71  const int8_t (*p)[2] = mv_ref_blk_off[b->bs];
72 #define INVALID_MV 0x80008000U
73  uint32_t mem = INVALID_MV, mem_sub8x8 = INVALID_MV;
74  int i;
75 
76 #define RETURN_DIRECT_MV(mv) \
77  do { \
78  uint32_t m = AV_RN32A(&mv); \
79  if (!idx) { \
80  AV_WN32A(pmv, m); \
81  return; \
82  } else if (mem == INVALID_MV) { \
83  mem = m; \
84  } else if (m != mem) { \
85  AV_WN32A(pmv, m); \
86  return; \
87  } \
88  } while (0)
89 
90  if (sb >= 0) {
91  if (sb == 2 || sb == 1) {
92  RETURN_DIRECT_MV(b->mv[0][z]);
93  } else if (sb == 3) {
94  RETURN_DIRECT_MV(b->mv[2][z]);
95  RETURN_DIRECT_MV(b->mv[1][z]);
96  RETURN_DIRECT_MV(b->mv[0][z]);
97  }
98 
99 #define RETURN_MV(mv) \
100  do { \
101  if (sb > 0) { \
102  VP56mv tmp; \
103  uint32_t m; \
104  av_assert2(idx == 1); \
105  av_assert2(mem != INVALID_MV); \
106  if (mem_sub8x8 == INVALID_MV) { \
107  clamp_mv(&tmp, &mv, td); \
108  m = AV_RN32A(&tmp); \
109  if (m != mem) { \
110  AV_WN32A(pmv, m); \
111  return; \
112  } \
113  mem_sub8x8 = AV_RN32A(&mv); \
114  } else if (mem_sub8x8 != AV_RN32A(&mv)) { \
115  clamp_mv(&tmp, &mv, td); \
116  m = AV_RN32A(&tmp); \
117  if (m != mem) { \
118  AV_WN32A(pmv, m); \
119  } else { \
120  /* BUG I'm pretty sure this isn't the intention */ \
121  AV_WN32A(pmv, 0); \
122  } \
123  return; \
124  } \
125  } else { \
126  uint32_t m = AV_RN32A(&mv); \
127  if (!idx) { \
128  clamp_mv(pmv, &mv, td); \
129  return; \
130  } else if (mem == INVALID_MV) { \
131  mem = m; \
132  } else if (m != mem) { \
133  clamp_mv(pmv, &mv, td); \
134  return; \
135  } \
136  } \
137  } while (0)
138 
139  if (row > 0) {
140  VP9mvrefPair *mv = &s->s.frames[CUR_FRAME].mv[(row - 1) * s->sb_cols * 8 + col];
141  if (mv->ref[0] == ref)
142  RETURN_MV(s->above_mv_ctx[2 * col + (sb & 1)][0]);
143  else if (mv->ref[1] == ref)
144  RETURN_MV(s->above_mv_ctx[2 * col + (sb & 1)][1]);
145  }
146  if (col > td->tile_col_start) {
147  VP9mvrefPair *mv = &s->s.frames[CUR_FRAME].mv[row * s->sb_cols * 8 + col - 1];
148  if (mv->ref[0] == ref)
149  RETURN_MV(td->left_mv_ctx[2 * row7 + (sb >> 1)][0]);
150  else if (mv->ref[1] == ref)
151  RETURN_MV(td->left_mv_ctx[2 * row7 + (sb >> 1)][1]);
152  }
153  i = 2;
154  } else {
155  i = 0;
156  }
157 
158  // previously coded MVs in this neighborhood, using same reference frame
159  for (; i < 8; i++) {
160  int c = p[i][0] + col, r = p[i][1] + row;
161 
162  if (c >= td->tile_col_start && c < s->cols &&
163  r >= 0 && r < s->rows) {
164  VP9mvrefPair *mv = &s->s.frames[CUR_FRAME].mv[r * s->sb_cols * 8 + c];
165 
166  if (mv->ref[0] == ref)
167  RETURN_MV(mv->mv[0]);
168  else if (mv->ref[1] == ref)
169  RETURN_MV(mv->mv[1]);
170  }
171  }
172 
173  // MV at this position in previous frame, using same reference frame
174  if (s->s.h.use_last_frame_mvs) {
175  VP9mvrefPair *mv = &s->s.frames[REF_FRAME_MVPAIR].mv[row * s->sb_cols * 8 + col];
176 
177  if (!s->s.frames[REF_FRAME_MVPAIR].uses_2pass)
178  ff_thread_await_progress(&s->s.frames[REF_FRAME_MVPAIR].tf, row >> 3, 0);
179  if (mv->ref[0] == ref)
180  RETURN_MV(mv->mv[0]);
181  else if (mv->ref[1] == ref)
182  RETURN_MV(mv->mv[1]);
183  }
184 
185 #define RETURN_SCALE_MV(mv, scale) \
186  do { \
187  if (scale) { \
188  VP56mv mv_temp = { -mv.x, -mv.y }; \
189  RETURN_MV(mv_temp); \
190  } else { \
191  RETURN_MV(mv); \
192  } \
193  } while (0)
194 
195  // previously coded MVs in this neighborhood, using different reference frame
196  for (i = 0; i < 8; i++) {
197  int c = p[i][0] + col, r = p[i][1] + row;
198 
199  if (c >= td->tile_col_start && c < s->cols && r >= 0 && r < s->rows) {
200  VP9mvrefPair *mv = &s->s.frames[CUR_FRAME].mv[r * s->sb_cols * 8 + c];
201 
202  if (mv->ref[0] != ref && mv->ref[0] >= 0)
203  RETURN_SCALE_MV(mv->mv[0],
204  s->s.h.signbias[mv->ref[0]] != s->s.h.signbias[ref]);
205  if (mv->ref[1] != ref && mv->ref[1] >= 0 &&
206  // BUG - libvpx has this condition regardless of whether
207  // we used the first ref MV and pre-scaling
208  AV_RN32A(&mv->mv[0]) != AV_RN32A(&mv->mv[1])) {
209  RETURN_SCALE_MV(mv->mv[1], s->s.h.signbias[mv->ref[1]] != s->s.h.signbias[ref]);
210  }
211  }
212  }
213 
214  // MV at this position in previous frame, using different reference frame
215  if (s->s.h.use_last_frame_mvs) {
216  VP9mvrefPair *mv = &s->s.frames[REF_FRAME_MVPAIR].mv[row * s->sb_cols * 8 + col];
217 
218  // no need to await_progress, because we already did that above
219  if (mv->ref[0] != ref && mv->ref[0] >= 0)
220  RETURN_SCALE_MV(mv->mv[0], s->s.h.signbias[mv->ref[0]] != s->s.h.signbias[ref]);
221  if (mv->ref[1] != ref && mv->ref[1] >= 0 &&
222  // BUG - libvpx has this condition regardless of whether
223  // we used the first ref MV and pre-scaling
224  AV_RN32A(&mv->mv[0]) != AV_RN32A(&mv->mv[1])) {
225  RETURN_SCALE_MV(mv->mv[1], s->s.h.signbias[mv->ref[1]] != s->s.h.signbias[ref]);
226  }
227  }
228 
229  AV_ZERO32(pmv);
230  clamp_mv(pmv, pmv, td);
231 #undef INVALID_MV
232 #undef RETURN_MV
233 #undef RETURN_SCALE_MV
234 }
235 
236 static av_always_inline int read_mv_component(VP9TileData *td, int idx, int hp)
237 {
238  VP9Context *s = td->s;
239  int bit, sign = vp56_rac_get_prob(td->c, s->prob.p.mv_comp[idx].sign);
241  s->prob.p.mv_comp[idx].classes);
242 
243  td->counts.mv_comp[idx].sign[sign]++;
244  td->counts.mv_comp[idx].classes[c]++;
245  if (c) {
246  int m;
247 
248  for (n = 0, m = 0; m < c; m++) {
249  bit = vp56_rac_get_prob(td->c, s->prob.p.mv_comp[idx].bits[m]);
250  n |= bit << m;
251  td->counts.mv_comp[idx].bits[m][bit]++;
252  }
253  n <<= 3;
255  s->prob.p.mv_comp[idx].fp);
256  n |= bit << 1;
257  td->counts.mv_comp[idx].fp[bit]++;
258  if (hp) {
259  bit = vp56_rac_get_prob(td->c, s->prob.p.mv_comp[idx].hp);
260  td->counts.mv_comp[idx].hp[bit]++;
261  n |= bit;
262  } else {
263  n |= 1;
264  // bug in libvpx - we count for bw entropy purposes even if the
265  // bit wasn't coded
266  td->counts.mv_comp[idx].hp[1]++;
267  }
268  n += 8 << c;
269  } else {
270  n = vp56_rac_get_prob(td->c, s->prob.p.mv_comp[idx].class0);
271  td->counts.mv_comp[idx].class0[n]++;
273  s->prob.p.mv_comp[idx].class0_fp[n]);
274  td->counts.mv_comp[idx].class0_fp[n][bit]++;
275  n = (n << 3) | (bit << 1);
276  if (hp) {
277  bit = vp56_rac_get_prob(td->c, s->prob.p.mv_comp[idx].class0_hp);
278  td->counts.mv_comp[idx].class0_hp[bit]++;
279  n |= bit;
280  } else {
281  n |= 1;
282  // bug in libvpx - we count for bw entropy purposes even if the
283  // bit wasn't coded
284  td->counts.mv_comp[idx].class0_hp[1]++;
285  }
286  }
287 
288  return sign ? -(n + 1) : (n + 1);
289 }
290 
291 void ff_vp9_fill_mv(VP9TileData *td, VP56mv *mv, int mode, int sb)
292 {
293  VP9Context *s = td->s;
294  VP9Block *b = td->b;
295 
296  if (mode == ZEROMV) {
297  AV_ZERO64(mv);
298  } else {
299  int hp;
300 
301  // FIXME cache this value and reuse for other subblocks
302  find_ref_mvs(td, &mv[0], b->ref[0], 0, mode == NEARMV,
303  mode == NEWMV ? -1 : sb);
304  // FIXME maybe move this code into find_ref_mvs()
305  if ((mode == NEWMV || sb == -1) &&
306  !(hp = s->s.h.highprecisionmvs &&
307  abs(mv[0].x) < 64 && abs(mv[0].y) < 64)) {
308  if (mv[0].y & 1) {
309  if (mv[0].y < 0)
310  mv[0].y++;
311  else
312  mv[0].y--;
313  }
314  if (mv[0].x & 1) {
315  if (mv[0].x < 0)
316  mv[0].x++;
317  else
318  mv[0].x--;
319  }
320  }
321  if (mode == NEWMV) {
323  s->prob.p.mv_joint);
324 
325  td->counts.mv_joint[j]++;
326  if (j >= MV_JOINT_V)
327  mv[0].y += read_mv_component(td, 0, hp);
328  if (j & 1)
329  mv[0].x += read_mv_component(td, 1, hp);
330  }
331 
332  if (b->comp) {
333  // FIXME cache this value and reuse for other subblocks
334  find_ref_mvs(td, &mv[1], b->ref[1], 1, mode == NEARMV,
335  mode == NEWMV ? -1 : sb);
336  if ((mode == NEWMV || sb == -1) &&
337  !(hp = s->s.h.highprecisionmvs &&
338  abs(mv[1].x) < 64 && abs(mv[1].y) < 64)) {
339  if (mv[1].y & 1) {
340  if (mv[1].y < 0)
341  mv[1].y++;
342  else
343  mv[1].y--;
344  }
345  if (mv[1].x & 1) {
346  if (mv[1].x < 0)
347  mv[1].x++;
348  else
349  mv[1].x--;
350  }
351  }
352  if (mode == NEWMV) {
354  s->prob.p.mv_joint);
355 
356  td->counts.mv_joint[j]++;
357  if (j >= MV_JOINT_V)
358  mv[1].y += read_mv_component(td, 0, hp);
359  if (j & 1)
360  mv[1].x += read_mv_component(td, 1, hp);
361  }
362  }
363  }
364 }
find_ref_mvs
static void find_ref_mvs(VP9TileData *td, VP56mv *pmv, int ref, int z, int idx, int sb)
Definition: vp9mvs.c:37
td
#define td
Definition: regdef.h:70
VP56mv::x
int16_t x
Definition: vp56.h:67
BS_64x64
@ BS_64x64
Definition: vp9shared.h:78
BS_64x32
@ BS_64x32
Definition: vp9shared.h:79
ff_vp9_mv_joint_tree
const int8_t ff_vp9_mv_joint_tree[3][2]
Definition: vp9data.c:2224
mv
static const int8_t mv[256][2]
Definition: 4xm.c:77
INVALID_MV
#define INVALID_MV
internal.h
MVJoint
MVJoint
Definition: vp9dec.h:41
b
#define b
Definition: input.c:41
ff_vp9_mv_class_tree
const int8_t ff_vp9_mv_class_tree[10][2]
Definition: vp9data.c:2230
RETURN_SCALE_MV
#define RETURN_SCALE_MV(mv, scale)
BS_4x8
@ BS_4x8
Definition: vp9shared.h:89
NEARMV
@ NEARMV
Definition: vp9shared.h:43
ff_thread_await_progress
the pkt_dts and pkt_pts fields in AVFrame will work as usual Restrictions on codec whose streams don t reset across will not work because their bitstreams cannot be decoded in parallel *The contents of buffers must not be read before ff_thread_await_progress() has been called on them. reget_buffer() and buffer age optimizations no longer work. *The contents of buffers must not be written to after ff_thread_report_progress() has been called on them. This includes draw_edges(). Porting codecs to frame threading
VP9Block
Definition: vp9dec.h:81
bit
#define bit(string, value)
Definition: cbs_mpeg2.c:58
BS_8x16
@ BS_8x16
Definition: vp9shared.h:86
x
FFmpeg Automated Testing Environment ************************************Introduction Using FATE from your FFmpeg source directory Submitting the results to the FFmpeg result aggregation server Uploading new samples to the fate suite FATE makefile targets and variables Makefile targets Makefile variables Examples Introduction **************FATE is an extended regression suite on the client side and a means for results aggregation and presentation on the server side The first part of this document explains how you can use FATE from your FFmpeg source directory to test your ffmpeg binary The second part describes how you can run FATE to submit the results to FFmpeg’s FATE server In any way you can have a look at the publicly viewable FATE results by visiting this as it can be seen if some test on some platform broke with their recent contribution This usually happens on the platforms the developers could not test on The second part of this document describes how you can run FATE to submit your results to FFmpeg’s FATE server If you want to submit your results be sure to check that your combination of OS and compiler is not already listed on the above mentioned website In the third part you can find a comprehensive listing of FATE makefile targets and variables Using FATE from your FFmpeg source directory **********************************************If you want to run FATE on your machine you need to have the samples in place You can get the samples via the build target fate rsync Use this command from the top level source this will cause FATE to fail NOTE To use a custom wrapper to run the pass ‘ target exec’ to ‘configure’ or set the TARGET_EXEC Make variable Submitting the results to the FFmpeg result aggregation server ****************************************************************To submit your results to the server you should run fate through the shell script ‘tests fate sh’ from the FFmpeg sources This script needs to be invoked with a configuration file as its first argument tests fate sh path to fate_config A configuration file template with comments describing the individual configuration variables can be found at ‘doc fate_config sh template’ Create a configuration that suits your based on the configuration template The ‘slot’ configuration variable can be any string that is not yet but it is suggested that you name it adhering to the following pattern ‘ARCH OS COMPILER COMPILER VERSION’ The configuration file itself will be sourced in a shell therefore all shell features may be used This enables you to setup the environment as you need it for your build For your first test runs the ‘fate_recv’ variable should be empty or commented out This will run everything as normal except that it will omit the submission of the results to the server The following files should be present in $workdir as specified in the configuration it may help to try out the ‘ssh’ command with one or more ‘ v’ options You should get detailed output concerning your SSH configuration and the authentication process The only thing left is to automate the execution of the fate sh script and the synchronisation of the samples directory Uploading new samples to the fate suite *****************************************If you need a sample uploaded send a mail to samples request This is for developers who have an account on the fate suite server If you upload new please make sure they are as small as space on each network bandwidth and so on benefit from smaller test cases Also keep in mind older checkouts use existing sample that means in practice generally do not remove or overwrite files as it likely would break older checkouts or releases Also all needed samples for a commit should be ideally before the push If you need an account for frequently uploading samples or you wish to help others by doing that send a mail to ffmpeg devel rsync vauL Duo x
Definition: fate.txt:150
ZEROMV
@ ZEROMV
Definition: vp9shared.h:44
ff_vp9_mv_fp_tree
const int8_t ff_vp9_mv_fp_tree[3][2]
Definition: vp9data.c:2243
BS_32x32
@ BS_32x32
Definition: vp9shared.h:81
s
#define s(width, name)
Definition: cbs_vp9.c:257
AV_ZERO64
#define AV_ZERO64(d)
Definition: intreadwrite.h:633
AV_ZERO32
#define AV_ZERO32(d)
Definition: intreadwrite.h:629
vp9data.h
RETURN_DIRECT_MV
#define RETURN_DIRECT_MV(mv)
VP56mv::y
int16_t y
Definition: vp56.h:68
VP56mv
Definition: vp56.h:66
BS_8x4
@ BS_8x4
Definition: vp9shared.h:88
vp56.h
if
if(ret)
Definition: filter_design.txt:179
VP9Context
Definition: vp9dec.h:93
vp8_rac_get_tree
static av_always_inline int vp8_rac_get_tree(VP56RangeCoder *c, const int8_t(*tree)[2], const uint8_t *probs)
Definition: vp56.h:394
REF_FRAME_MVPAIR
#define REF_FRAME_MVPAIR
Definition: vp9shared.h:164
src
#define src
Definition: vp8dsp.c:254
BS_32x64
@ BS_32x64
Definition: vp9shared.h:80
abs
#define abs(x)
Definition: cuda_runtime.h:35
MV_JOINT_V
@ MV_JOINT_V
Definition: vp9dec.h:44
c
Undefined Behavior In the C some operations are like signed integer dereferencing freed accessing outside allocated Undefined Behavior must not occur in a C it is not safe even if the output of undefined operations is unused The unsafety may seem nit picking but Optimizing compilers have in fact optimized code on the assumption that no undefined Behavior occurs Optimizing code based on wrong assumptions can and has in some cases lead to effects beyond the output of computations The signed integer overflow problem in speed critical code Code which is highly optimized and works with signed integers sometimes has the problem that often the output of the computation does not c
Definition: undefined.txt:32
vp9.h
BS_8x8
@ BS_8x8
Definition: vp9shared.h:87
N_BS_SIZES
@ N_BS_SIZES
Definition: vp9shared.h:91
BS_4x4
@ BS_4x4
Definition: vp9shared.h:90
r
#define r
Definition: input.c:40
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:269
BS_16x32
@ BS_16x32
Definition: vp9shared.h:83
av_always_inline
#define av_always_inline
Definition: attributes.h:49
AV_RN32A
#define AV_RN32A(p)
Definition: intreadwrite.h:526
read_mv_component
static av_always_inline int read_mv_component(VP9TileData *td, int idx, int hp)
Definition: vp9mvs.c:236
VP9mvrefPair
Definition: vp9shared.h:54
clamp_mv
static av_always_inline void clamp_mv(VP56mv *dst, const VP56mv *src, VP9TileData *td)
Definition: vp9mvs.c:30
VP9TileData
Definition: vp9dec.h:163
NEWMV
@ NEWMV
Definition: vp9shared.h:45
BS_16x16
@ BS_16x16
Definition: vp9shared.h:84
ff_vp9_fill_mv
void ff_vp9_fill_mv(VP9TileData *td, VP56mv *mv, int mode, int sb)
Definition: vp9mvs.c:291
BS_16x8
@ BS_16x8
Definition: vp9shared.h:85
mode
mode
Definition: ebur128.h:83
ref
static int ref[MAX_W *MAX_W]
Definition: jpeg2000dwt.c:107
vp9dec.h
vp56_rac_get_prob
#define vp56_rac_get_prob
Definition: vp56.h:268
CUR_FRAME
#define CUR_FRAME
Definition: vp9shared.h:163
BS_32x16
@ BS_32x16
Definition: vp9shared.h:82
RETURN_MV
#define RETURN_MV(mv)