1 | IPhone 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 | |
---|
3 | From: Dave <dave@Asus-Laptop.(none)> |
---|
4 | |
---|
5 | The flip is executed differently depending on the decoding/rendering method: |
---|
6 | |
---|
7 | VDPAU: 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 | |
---|
9 | Test both active types and it's working well for me |
---|
10 | |
---|
11 | This 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 | |
---|
29 | diff --git a/mythtv/filters/filters.pro b/mythtv/filters/filters.pro |
---|
30 | index 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 | |
---|
41 | diff --git a/mythtv/filters/vflip/filter_vflip.c b/mythtv/filters/vflip/filter_vflip.c |
---|
42 | new file mode 100644 |
---|
43 | index 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 | +};
|
---|
199 | diff --git a/mythtv/filters/vflip/vflip.pro b/mythtv/filters/vflip/vflip.pro |
---|
200 | new file mode 100644 |
---|
201 | index 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 |
---|
214 | diff --git a/mythtv/libs/libmythtv/avformatdecoder.cpp b/mythtv/libs/libmythtv/avformatdecoder.cpp |
---|
215 | index 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; |
---|
237 | diff --git a/mythtv/libs/libmythtv/decoderbase.cpp b/mythtv/libs/libmythtv/decoderbase.cpp |
---|
238 | index 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)); |
---|
251 | diff --git a/mythtv/libs/libmythtv/decoderbase.h b/mythtv/libs/libmythtv/decoderbase.h |
---|
252 | index 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; |
---|
271 | diff --git a/mythtv/libs/libmythtv/mythplayer.cpp b/mythtv/libs/libmythtv/mythplayer.cpp |
---|
272 | index 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(); |
---|
296 | diff --git a/mythtv/libs/libmythtv/videoout_vdpau.cpp b/mythtv/libs/libmythtv/videoout_vdpau.cpp |
---|
297 | index 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 | +} |
---|
314 | diff --git a/mythtv/libs/libmythtv/videoout_vdpau.h b/mythtv/libs/libmythtv/videoout_vdpau.h |
---|
315 | index 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; } |
---|
326 | diff --git a/mythtv/libs/libmythtv/videooutbase.h b/mythtv/libs/libmythtv/videooutbase.h |
---|
327 | index 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, |
---|
340 | diff --git a/mythtv/libs/libmythui/mythrender_vdpau.cpp b/mythtv/libs/libmythui/mythrender_vdpau.cpp |
---|
341 | index 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, |
---|
386 | diff --git a/mythtv/libs/libmythui/mythrender_vdpau.h b/mythtv/libs/libmythui/mythrender_vdpau.h |
---|
387 | index 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; |
---|