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 Badia <dbadia@gmail.com> |
---|
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 |
---|
8 | Software: Added a new filter called vflip. This filter will be automatically applied when needed. |
---|
9 | XVMC: Didn't bother since it's going away |
---|
10 | |
---|
11 | Test both active types and it's working well for me |
---|
12 | |
---|
13 | This 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 | |
---|
33 | diff --git a/mythtv/filters/filters.pro b/mythtv/filters/filters.pro |
---|
34 | index 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 | |
---|
45 | diff --git a/mythtv/filters/vflip/filter_vflip.c b/mythtv/filters/vflip/filter_vflip.c |
---|
46 | new file mode 100644 |
---|
47 | index 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 | +}; |
---|
194 | diff --git a/mythtv/filters/vflip/vflip.pro b/mythtv/filters/vflip/vflip.pro |
---|
195 | new file mode 100644 |
---|
196 | index 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 |
---|
209 | diff --git a/mythtv/libs/libmythtv/avformatdecoder.cpp b/mythtv/libs/libmythtv/avformatdecoder.cpp |
---|
210 | index 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; |
---|
232 | diff --git a/mythtv/libs/libmythtv/decoderbase.cpp b/mythtv/libs/libmythtv/decoderbase.cpp |
---|
233 | index 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)); |
---|
246 | diff --git a/mythtv/libs/libmythtv/decoderbase.h b/mythtv/libs/libmythtv/decoderbase.h |
---|
247 | index 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]; |
---|
266 | diff --git a/mythtv/libs/libmythtv/mythplayer.cpp b/mythtv/libs/libmythtv/mythplayer.cpp |
---|
267 | index 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(); |
---|
291 | diff --git a/mythtv/libs/libmythtv/videoout_vdpau.cpp b/mythtv/libs/libmythtv/videoout_vdpau.cpp |
---|
292 | index 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 | +} |
---|
309 | diff --git a/mythtv/libs/libmythtv/videoout_vdpau.h b/mythtv/libs/libmythtv/videoout_vdpau.h |
---|
310 | index 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; } |
---|
321 | diff --git a/mythtv/libs/libmythtv/videooutbase.h b/mythtv/libs/libmythtv/videooutbase.h |
---|
322 | index 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, |
---|
335 | diff --git a/mythtv/libs/libmythui/mythrender_vdpau.cpp b/mythtv/libs/libmythui/mythrender_vdpau.cpp |
---|
336 | index 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(); |
---|
377 | diff --git a/mythtv/libs/libmythui/mythrender_vdpau.h b/mythtv/libs/libmythui/mythrender_vdpau.h |
---|
378 | index 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; |
---|