Ticket #9861: mov-flip-both

File mov-flip-both, 13.3 KB (added by Dave Badia <dbadia@…>, 13 years ago)
Line 
1IPhone videos can appear upside down in myth depending on the orientation of the phone when the video was captured.  This patch checks the MOV metadata to detect the orientation and flips the frames if necessary.  The MOV metadata is read by ffmpeg so this upstream ffmpeg patch is required to be merged into myth: http://git.videolan.org/?p=ffmpeg.git;a=commit;h=d3cef0a85b7d3fd133a3349341646fe15aeb0030
2
3From: Dave Badia <dbadia@gmail.com>
4
5The flip is executed differently depending on the decoding/rendering method:
6
7VDPAU: Simple swap of coordinates as suggested by the nvidia documentation
8Software: Added a new filter called vflip.  This filter will be automatically applied when needed.
9XVMC: Didn't bother since it's going away
10
11Test both active types and it's working well for me
12
13This patch can be safely merged into myth prior to the ffmpeg patch - it just won't activate until that's done.
14---
15
16 mythtv/filters/filters.pro                 |    1
17 mythtv/filters/vflip/filter_vflip.c        |  143 ++++++++++++++++++++++++++++
18 mythtv/filters/vflip/vflip.pro             |    8 ++
19 mythtv/libs/libmythtv/avformatdecoder.cpp  |    5 +
20 mythtv/libs/libmythtv/decoderbase.cpp      |    3 -
21 mythtv/libs/libmythtv/decoderbase.h        |    2
22 mythtv/libs/libmythtv/mythplayer.cpp       |    7 +
23 mythtv/libs/libmythtv/videoout_vdpau.cpp   |   10 ++
24 mythtv/libs/libmythtv/videoout_vdpau.h     |    1
25 mythtv/libs/libmythtv/videooutbase.h       |    3 +
26 mythtv/libs/libmythui/mythrender_vdpau.cpp |   24 ++++-
27 mythtv/libs/libmythui/mythrender_vdpau.h   |    2
28 12 files changed, 203 insertions(+), 6 deletions(-)
29 create mode 100644 mythtv/filters/vflip/filter_vflip.c
30 create mode 100644 mythtv/filters/vflip/vflip.pro
31
32
33diff --git a/mythtv/filters/filters.pro b/mythtv/filters/filters.pro
34index eb62c98..195d209 100644
35--- a/mythtv/filters/filters.pro
36+++ b/mythtv/filters/filters.pro
37@@ -5,6 +5,7 @@ TEMPLATE = subdirs
38 # Directories
39 SUBDIRS += invert linearblend denoise3d quickdnr kerneldeint crop force
40 SUBDIRS += adjust onefield bobdeint ivtc greedyhdeint yadif fieldorder
41+SUBDIRS += vflip
42 
43 contains(CONFIG_POSTPROC, yes): SUBDIRS += postprocess
44 
45diff --git a/mythtv/filters/vflip/filter_vflip.c b/mythtv/filters/vflip/filter_vflip.c
46new file mode 100644
47index 0000000..232f214
48--- /dev/null
49+++ b/mythtv/filters/vflip/filter_vflip.c
50@@ -0,0 +1,143 @@
51+/*
52+ *  filter_vflip
53+ */
54+
55+#include <stdio.h>
56+#include <stdlib.h>
57+#include <unistd.h>
58+
59+#include "filter.h"
60+#include "frame.h"
61+
62+typedef struct ThisFilter
63+{
64+    VideoFilter vf;
65+    TF_STRUCT;
66+} ThisFilter;
67+
68+void reverse_memcpy(unsigned char *dst, const unsigned char *src, int n)
69+{
70+    int i;
71+    for (i=0; i < n; ++i)
72+        dst[n-1-i] = src[i];
73+}
74+
75+int swap(VideoFrame *frame, int datasize, int offset)
76+{
77+    int i, j;
78+    int oldoffset;
79+    int newoffset;
80+    unsigned char *temp = malloc(datasize);
81+    if (temp == NULL)
82+    {
83+        fprintf(stderr,"Couldn't allocate memory for temp\n");
84+        return NULL;
85+    }
86+    for (i = 0, j=(frame->height-1); i < frame->height/2; i++, j--) {
87+        newoffset = i * datasize + offset;
88+        oldoffset = j * datasize + offset;
89+        if (i != frame->height-1) {
90+            memcpy(temp, frame->buf + newoffset, datasize); // new -> temp
91+            reverse_memcpy(frame->buf + newoffset, frame->buf + oldoffset , datasize); // old -> new
92+            reverse_memcpy(frame->buf + oldoffset, temp, datasize); // temp -> old
93+        }
94+    }
95+    if (temp)
96+        free(temp);
97+
98+    return 1;
99+}
100+
101+int comp(const int *a,const int *b)
102+{
103+       if (*a==*b)
104+               return 0;
105+       else if (*a < *b)
106+               return -1;
107+       else
108+               return 1;
109+}
110+
111+int vflip(VideoFilter *vf, VideoFrame *frame, int field)
112+{
113+    (void)field;
114+
115+    if (frame->codec != FMT_YV12) {
116+        fprintf(stderr,"codec %d unsupported for vflip, skipping\n",
117+                frame->codec);
118+        return NULL;
119+    }
120+
121+    TF_VARS;
122+
123+    (void)vf;
124+
125+    TF_START;
126+
127+    int ouroffsets[3];
128+    memcpy(ouroffsets, frame->offsets, 3 * sizeof(int));
129+    qsort(ouroffsets, 3, sizeof(int), comp);
130+
131+    int datasize;
132+    datasize = (ouroffsets[1] - ouroffsets[0]) / frame->height;
133+    if (!swap(frame, datasize, ouroffsets[0]))
134+        return NULL;
135+    datasize = (ouroffsets[2] - ouroffsets[1]) / frame->height;
136+    if (!swap(frame, datasize, ouroffsets[1]))
137+        return NULL;
138+    datasize = (frame->size - ouroffsets[2]) / frame->height;
139+    if (!swap(frame, datasize, ouroffsets[2]))
140+        return NULL;
141+
142+    TF_END((ThisFilter *)vf, "VFlip");
143+
144+    return 1;
145+}
146+
147+static VideoFilter *new_filter(VideoFrameType inpixfmt,
148+                               VideoFrameType outpixfmt,
149+                               int *width, int *height, char *options,
150+                               int threads)
151+{
152+    ThisFilter *filter;
153+
154+    (void)width;
155+    (void)height;
156+    (void)options;
157+    (void)threads;
158+
159+    if ((inpixfmt != outpixfmt) ||
160+        (inpixfmt != FMT_YV12 && inpixfmt != FMT_RGB24 &&
161+         inpixfmt != FMT_YUV422P) )
162+        return NULL;
163+
164+    filter = malloc(sizeof(ThisFilter));
165+
166+    if (filter == NULL)
167+    {
168+        fprintf(stderr,"Couldn't allocate memory for filter\n");
169+        return NULL;
170+    }
171+    filter->vf.filter = &vflip;
172+    filter->vf.cleanup = NULL;
173+    TF_INIT(filter)
174+    return (VideoFilter *) filter;
175+}
176+
177+static FmtConv FmtList[] =
178+{
179+    { FMT_YV12, FMT_YV12 },
180+    FMT_NULL
181+};
182+
183+ConstFilterInfo filter_table[] =
184+{
185+    {
186+        filter_init: &new_filter,
187+        name:       "vflip",
188+        descript:   "flips the video image vertically",
189+        formats:    FmtList,
190+        libname:    NULL
191+    },
192+    FILT_NULL
193+};
194diff --git a/mythtv/filters/vflip/vflip.pro b/mythtv/filters/vflip/vflip.pro
195new file mode 100644
196index 0000000..946f5e7
197--- /dev/null
198+++ b/mythtv/filters/vflip/vflip.pro
199@@ -0,0 +1,8 @@
200+include ( ../filter-common.pro )
201+include ( ../filter-avcodec.pro )
202+INCLUDEPATH += ../../libs/libmythtv \
203+    ../../libs/libavcodec \
204+    ../..
205+
206+# Input
207+SOURCES += filter_vflip.c
208\ No newline at end of file
209diff --git a/mythtv/libs/libmythtv/avformatdecoder.cpp b/mythtv/libs/libmythtv/avformatdecoder.cpp
210index 3bceff4..8d246c5 100644
211--- a/mythtv/libs/libmythtv/avformatdecoder.cpp
212+++ b/mythtv/libs/libmythtv/avformatdecoder.cpp
213@@ -65,6 +65,7 @@ extern const uint8_t *ff_find_start_code(const uint8_t *p, const uint8_t *end, u
214 extern void ff_read_frame_flush(AVFormatContext *s);
215 #include "libavformat/avio.h"
216 #include "libswscale/swscale.h"
217+#include "libavformat/isom.h"
218 #include "ivtv_myth.h"
219 }
220 
221@@ -1267,6 +1268,10 @@ void AvFormatDecoder::InitVideoCodec(AVStream *stream, AVCodecContext *enc,
222         }
223     }
224 
225+    AVMetadataTag *metatag = av_metadata_get(stream->metadata, "rotate", NULL, 0);
226+    if (metatag && metatag->value && QString("180") == metatag->value)
227+        video_inverted = true;
228+
229     if (CODEC_IS_VDPAU(codec))
230     {
231         enc->get_buffer      = get_avf_buffer_vdpau;
232diff --git a/mythtv/libs/libmythtv/decoderbase.cpp b/mythtv/libs/libmythtv/decoderbase.cpp
233index f42ca38..cfc0a4c 100644
234--- a/mythtv/libs/libmythtv/decoderbase.cpp
235+++ b/mythtv/libs/libmythtv/decoderbase.cpp
236@@ -46,7 +46,8 @@ DecoderBase::DecoderBase(MythPlayer *parent, const ProgramInfo &pginfo)
237       errored(false), waitingForChange(false), readAdjust(0),
238       justAfterChange(false),
239       // language preference
240-      languagePreference(iso639_get_language_key_list())
241+      languagePreference(iso639_get_language_key_list()),
242+      video_inverted(false)
243 {
244     ResetTracks();
245     tracks[kTrackTypeAudio].push_back(StreamInfo(0, 0, 0, 0, 0));
246diff --git a/mythtv/libs/libmythtv/decoderbase.h b/mythtv/libs/libmythtv/decoderbase.h
247index b1d0508..ad9365d 100644
248--- a/mythtv/libs/libmythtv/decoderbase.h
249+++ b/mythtv/libs/libmythtv/decoderbase.h
250@@ -204,6 +204,7 @@ class DecoderBase
251 
252     void SaveTotalDuration(void);
253     void ResetTotalDuration(void) { totalDuration = 0; }
254+    bool GetVideoInverted(void) const { return video_inverted; }
255 
256   protected:
257     virtual int  AutoSelectTrack(uint type);
258@@ -274,6 +275,7 @@ class DecoderBase
259     bool waitingForChange;
260     long long readAdjust;
261     bool justAfterChange;
262+    bool video_inverted;
263 
264     // Audio/Subtitle/EIA-608/EIA-708 stream selection
265     int         currentTrack[kTrackTypeCount];
266diff --git a/mythtv/libs/libmythtv/mythplayer.cpp b/mythtv/libs/libmythtv/mythplayer.cpp
267index c374956..b2fc678 100644
268--- a/mythtv/libs/libmythtv/mythplayer.cpp
269+++ b/mythtv/libs/libmythtv/mythplayer.cpp
270@@ -527,6 +527,9 @@ bool MythPlayer::InitVideo(void)
271     if (embedding && pipState == kPIPOff)
272         videoOutput->EmbedInWidget(embedRect);
273 
274+       if (decoder && decoder->GetVideoInverted())
275+               videoOutput->SetVideoFlip();
276+
277     InitFilters();
278 
279     return true;
280@@ -1060,6 +1063,10 @@ void MythPlayer::InitFilters(void)
281     if (!videoFiltersOverride.isEmpty())
282         filters = videoFiltersOverride;
283 
284+    AvFormatDecoder *afd = dynamic_cast<AvFormatDecoder *>(decoder);
285+    if (afd && afd->GetVideoInverted() && !filters.contains("vflip"))
286+        filters += ",vflip";
287+
288     filters.detach();
289 
290     videofiltersLock.lock();
291diff --git a/mythtv/libs/libmythtv/videoout_vdpau.cpp b/mythtv/libs/libmythtv/videoout_vdpau.cpp
292index fd10407..8f2de5a 100644
293--- a/mythtv/libs/libmythtv/videoout_vdpau.cpp
294+++ b/mythtv/libs/libmythtv/videoout_vdpau.cpp
295@@ -1276,3 +1276,13 @@ bool VideoOutputVDPAU::GetScreenShot(int width, int height, QString filename)
296         return m_render->GetScreenShot(width, height, filename);
297     return false;
298 }
299+
300+void VideoOutputVDPAU::SetVideoFlip(void)
301+{
302+    if (!m_render)
303+    {
304+        VERBOSE(VB_PLAYBACK, LOC_ERR + QString("SetVideoFlip failed."));
305+        return;
306+    }
307+    m_render->SetVideoFlip();
308+}
309diff --git a/mythtv/libs/libmythtv/videoout_vdpau.h b/mythtv/libs/libmythtv/videoout_vdpau.h
310index 2404bb3..3be7daa 100644
311--- a/mythtv/libs/libmythtv/videoout_vdpau.h
312+++ b/mythtv/libs/libmythtv/videoout_vdpau.h
313@@ -68,6 +68,7 @@ class VideoOutputVDPAU : public VideoOutput
314         { return VideoOutput::CanVisualise(audio, m_render);       }
315     virtual bool SetupVisualisation(AudioPlayer *audio, MythRender *render)
316         { return VideoOutput::SetupVisualisation(audio, m_render); }
317+    virtual void SetVideoFlip(void);
318 
319   private:
320     virtual bool hasFullScreenOSD(void) const { return true; }
321diff --git a/mythtv/libs/libmythtv/videooutbase.h b/mythtv/libs/libmythtv/videooutbase.h
322index 334d08c..be1090a 100644
323--- a/mythtv/libs/libmythtv/videooutbase.h
324+++ b/mythtv/libs/libmythtv/videooutbase.h
325@@ -225,6 +225,9 @@ class VideoOutput
326     /// \brief check if video underscan/overscan is allowed
327     bool IsVideoScalingAllowed(void) const;
328 
329+    /// \brief Tells the player to flip the video frames for proper display
330+    virtual void SetVideoFlip(void) { };
331+
332     /// \brief returns QRect of PIP based on PIPLocation
333     virtual QRect GetPIPRect(PIPLocation location,
334                              MythPlayer *pipplayer = NULL,
335diff --git a/mythtv/libs/libmythui/mythrender_vdpau.cpp b/mythtv/libs/libmythui/mythrender_vdpau.cpp
336index 8472b68..9cb4472 100644
337--- a/mythtv/libs/libmythui/mythrender_vdpau.cpp
338+++ b/mythtv/libs/libmythui/mythrender_vdpau.cpp
339@@ -260,7 +260,8 @@ MythRenderVDPAU::MythRenderVDPAU()
340     m_render_lock(QMutex::Recursive), m_decode_lock(QMutex::Recursive),
341     m_display(NULL), m_window(0), m_device(0), m_surface(0),
342     m_flipQueue(0),  m_flipTarget(0), m_flipReady(false), m_colorKey(0),
343-    vdp_get_proc_address(NULL), vdp_get_error_string(NULL)
344+    vdp_get_proc_address(NULL), vdp_get_error_string(NULL),
345+    m_flipFrames(false)
346 {
347     LOCK_ALL
348     ResetProcs();
349@@ -1077,10 +1078,23 @@ bool MythRenderVDPAU::MixAndRend(uint id, VdpVideoMixerPictureStructure field,
350     outRect.y0    = dst.top();
351     outRect.x1    = dst.left() + dst.width();
352     outRect.y1    = dst.top()  + dst.height();
353-    srcRect.x0    = src.left();
354-    srcRect.y0    = src.top();
355-    srcRect.x1    = src.left() + src.width();
356-    srcRect.y1    = src.top() +  src.height();
357+
358+    if (m_flipFrames)
359+    {
360+        // flip the image
361+        srcRect.x0    = src.left() + src.width();
362+        srcRect.y0    = src.top() +  src.height();
363+        srcRect.x1    = src.left();
364+        srcRect.y1    = src.top();
365+    }
366+    else
367+    {
368+        srcRect.x0    = src.left();
369+        srcRect.y0    = src.top();
370+        srcRect.x1    = src.left() + src.width();
371+        srcRect.y1    = src.top() +  src.height();
372+    }
373+
374     outRectVid.x0 = dst_vid.left();
375     outRectVid.y0 = dst_vid.top();
376     outRectVid.x1 = dst_vid.left() + dst_vid.width();
377diff --git a/mythtv/libs/libmythui/mythrender_vdpau.h b/mythtv/libs/libmythui/mythrender_vdpau.h
378index be673dc..e39b18b 100644
379--- a/mythtv/libs/libmythui/mythrender_vdpau.h
380+++ b/mythtv/libs/libmythui/mythrender_vdpau.h
381@@ -125,6 +125,7 @@ class MUI_PUBLIC MythRenderVDPAU : public MythRender
382     void  ChangeVideoSurfaceOwner(uint id);
383 
384     void  Decode(uint id, struct vdpau_render_state *render);
385+    void  SetVideoFlip(void)        { m_flipFrames = true; }
386 
387   private:
388     bool CreateDevice(void);
389@@ -169,6 +170,7 @@ class MUI_PUBLIC MythRenderVDPAU : public MythRender
390     VdpPresentationQueueTarget       m_flipTarget;
391     bool                             m_flipReady;
392     uint                             m_colorKey;
393+    bool                             m_flipFrames;
394 
395     QVector<uint>                    m_surfaces;
396     QHash<uint, VDPAUOutputSurface>  m_outputSurfaces;