Ticket #9861: mov-flip-both2

File mov-flip-both2, 13.9 KB (added by Dave Badia <dbadia@…>, 8 years ago)

new patch that applies cleanly to master

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