Repository: gradio-app/gradio

Issue:
Video mirroring
### Describe the bug

Upload a video, and the output video is a mirror image.



### Is there an existing issue for this?

- [X] I have searched the existing issues

### Reproduction

```python
import gradio as gr

def video_test(video):
    return video

inputs_video_p5 = gr.Video(format="mp4", source="upload", label="原始视频")  # webcam
outputs_webcam_p5 = gr.Video(format='mp4', label="检测视频")

gyd = gr.Interface(
    fn=video_test,
    inputs=[inputs_video_p5],
    outputs=[outputs_webcam_p5],
)
gyd.launch(
    inbrowser=True,
)
```

### Screenshot

![ksnip_20220716-145557](https://user-images.githubusercontent.com/41098760/179344052-89c2c5be-2256-4ad1-aeef-125d42b1455e.png)


### Logs

```shell
see image above
```


### System Info

```shell
gradio 3.0.25
ubuntu 20.04
chrome
```


### Severity

annoying


Discussion:
Hi @Zengyf-CVer yes this is the default behavior of the webcam component, but can be changed by setting `mirror_webcam=False` in the `Video()`  constructor
How do we enable the following behavior?
- if the video is from webcam, mirror it
- if the video is selected from example, do not mirror it
Thanks @mistycheney, that seems like reasonable default behavior to me, what do you think @pngwn?
Yeah, that really should be the default, i kinda thought it was. Current behaviour feels like a bug.
I can't reproduce this on `main` with the example code. I can also confirm that only webcam videos are mirrored nothing else.

https://user-images.githubusercontent.com/12937446/195094030-32702d67-2459-4a7b-bc63-3639c86c201f.mov

We have had multiple reports of this now though, so I'm not sure is happening.

For what its worth, I do get the flipping behavior running on Windows with ffmpeg version 4.4

<img width="961" alt="image" src="https://user-images.githubusercontent.com/1778297/195966062-9dc8e144-2ccb-4ffd-80a9-e14d8010397b.png">

If someone who can reproduce this can pick it up, that would be good. I'm not seeing this behaviour. Not sure why.

The problem will be in the backend (in the pre/postprocessor) if it exists.
I think I know what's happening. Will put up a PR today and tag you @pngwn ! 
How do you debug these mysterious issues @freddyaboulton  👀 

Resolving patch:
diff --git a/gradio/components.py b/gradio/components.py
--- a/gradio/components.py
+++ b/gradio/components.py
@@ -1640,25 +1640,26 @@ def preprocess(self, x: Dict[str, str] | None) -> str | None:
                 file_data, file_path=file_name
             )
 
-        file_name = file.name
-        uploaded_format = file_name.split(".")[-1].lower()
-
-        if self.format is not None and uploaded_format != self.format:
-            output_file_name = file_name[0 : file_name.rindex(".") + 1] + self.format
-            ff = FFmpeg(inputs={file_name: None}, outputs={output_file_name: None})
-            ff.run()
-            return output_file_name
-        elif self.source == "webcam" and self.mirror_webcam is True:
-            path = Path(file_name)
-            output_file_name = str(path.with_stem(f"{path.stem}_flip"))
+        file_name = Path(file.name)
+        uploaded_format = file_name.suffix.replace(".", "")
+
+        modify_format = self.format is not None and uploaded_format != self.format
+        flip = self.source == "webcam" and self.mirror_webcam
+        if modify_format or flip:
+            format = f".{self.format if modify_format else uploaded_format}"
+            output_options = ["-vf", "hflip", "-c:a", "copy"] if flip else None
+            flip_suffix = "_flip" if flip else ""
+            output_file_name = str(
+                file_name.with_name(f"{file_name.stem}{flip_suffix}{format}")
+            )
             ff = FFmpeg(
-                inputs={file_name: None},
-                outputs={output_file_name: ["-vf", "hflip", "-c:a", "copy"]},
+                inputs={str(file_name): None},
+                outputs={output_file_name: output_options},
             )
             ff.run()
             return output_file_name
         else:
-            return file_name
+            return str(file_name)
 
     def generate_sample(self):
         """Generates a random video for testing the API."""


Test patch:
diff --git a/test/test_components.py b/test/test_components.py
--- a/test/test_components.py
+++ b/test/test_components.py
@@ -1234,7 +1234,9 @@ def test_component_functions(self):
         x_video["is_example"] = True
         self.assertIsNotNone(video_input.preprocess(x_video))
         video_input = gr.Video(format="avi")
-        self.assertEqual(video_input.preprocess(x_video)[-3:], "avi")
+        output_video = video_input.preprocess(x_video)
+        self.assertEqual(output_video[-3:], "avi")
+        assert "flip" not in output_video
 
         assert filecmp.cmp(
             video_input.serialize(x_video["name"])["name"], x_video["name"]
@@ -1291,6 +1293,45 @@ def test_video_postprocess_converts_to_playable_format(self):
             )
             assert processing_utils.video_is_playable(str(full_path_to_output))
 
+    @patch("gradio.components.FFmpeg")
+    def test_video_preprocessing_flips_video_for_webcam(self, mock_ffmpeg):
+        x_video = deepcopy(media_data.BASE64_VIDEO)
+        video_input = gr.Video(source="webcam")
+        _ = video_input.preprocess(x_video)
+
+        # Dict mapping filename to FFmpeg options
+        output_params = mock_ffmpeg.call_args_list[0][1]["outputs"]
+        assert "hflip" in list(output_params.values())[0]
+        assert "flip" in list(output_params.keys())[0]
+
+        mock_ffmpeg.reset_mock()
+        _ = gr.Video(source="webcam", mirror_webcam=False).preprocess(x_video)
+        mock_ffmpeg.assert_not_called()
+
+        mock_ffmpeg.reset_mock()
+        _ = gr.Video(source="upload", format="mp4").preprocess(x_video)
+        mock_ffmpeg.assert_not_called()
+
+        mock_ffmpeg.reset_mock()
+        output_file = gr.Video(
+            source="webcam", mirror_webcam=True, format="avi"
+        ).preprocess(x_video)
+        output_params = mock_ffmpeg.call_args_list[0][1]["outputs"]
+        assert "hflip" in list(output_params.values())[0]
+        assert "flip" in list(output_params.keys())[0]
+        assert ".avi" in list(output_params.keys())[0]
+        assert ".avi" in output_file
+
+        mock_ffmpeg.reset_mock()
+        output_file = gr.Video(
+            source="webcam", mirror_webcam=False, format="avi"
+        ).preprocess(x_video)
+        output_params = mock_ffmpeg.call_args_list[0][1]["outputs"]
+        assert list(output_params.values())[0] is None
+        assert "flip" not in list(output_params.keys())[0]
+        assert ".avi" in list(output_params.keys())[0]
+        assert ".avi" in output_file
+
 
 class TestTimeseries(unittest.TestCase):
     def test_component_functions(self):