| | 2618 | // Debug method for DetectLetterbox |
| | 2619 | // RED: 76, 109, 207 |
| | 2620 | // GREEN: 150, 91, 62 |
| | 2621 | void draw(VideoFrame *frame,int x, int y, unsigned char Y, unsigned char U, unsigned char V) { |
| | 2622 | unsigned char *buf = frame->buf; |
| | 2623 | int *pitches = frame->pitches; // Y, U, & V pitches |
| | 2624 | int *offsets = frame->offsets; // Y, U, & V offsets |
| | 2625 | |
| | 2626 | buf[offsets[0] + y * pitches[0] + x] = Y; |
| | 2627 | buf[offsets[1] + (y>>1) * pitches[1] + (x>>1)] = U; |
| | 2628 | buf[offsets[2] + (y>>1) * pitches[2] + (x>>1)] = V; |
| | 2629 | } |
| | 2630 | |
| | 2631 | void NuppelVideoPlayer::DetectLetterbox(VideoFrame *frame) |
| | 2632 | { |
| | 2633 | |
| | 2634 | unsigned char *buf = frame->buf; |
| | 2635 | int width = frame->width; |
| | 2636 | int height = frame->height; |
| | 2637 | int pitch = frame->pitches[0]; |
| | 2638 | int offset = frame->offsets[0]; |
| | 2639 | |
| | 2640 | |
| | 2641 | const int THRESHOLD = 5; // Y component has to not vary more than this in the bars |
| | 2642 | const int SWITCH_DELAY = 3; // How many frames of posetive detection to wait before switching |
| | 2643 | const int HORIZONTAL_THRESHOLD = 4; // How tolerant are we that the image has horizontal edges |
| | 2644 | const int NUMBER_OF_DETECTION_LINES = 3; // How many lines are we looking at |
| | 2645 | |
| | 2646 | // If the black bars is larger than this limit we switch to Half or Full Mode |
| | 2647 | const int fullLimit = (int) (((height - width * 9 / 16) / 2) * detectLetterboxLimit / 100); |
| | 2648 | const int halfLimit = (int) (((height - width * 9 / 14) / 2) * detectLetterboxLimit / 100); |
| | 2649 | |
| | 2650 | int xPos[] = {width / 4, width / 2, width * 3 / 4}; // Lines to scan for black letterbox edge |
| | 2651 | int topHits = 0, bottomHits = 0, minTop = 0, minBottom = 0, maxTop = 0, maxBottom = 0; |
| | 2652 | int topHit[] = {0, 0, 0}, bottomHit[] = {0, 0, 0}; |
| | 2653 | |
| | 2654 | int x,y; |
| | 2655 | int sourceAspect = width * 10 / height; |
| | 2656 | |
| | 2657 | |
| | 2658 | |
| | 2659 | if (!isDetectLetterbox) |
| | 2660 | return; |
| | 2661 | if (!videoOutput) |
| | 2662 | return; |
| | 2663 | |
| | 2664 | |
| | 2665 | if (frame->codec != FMT_YV12) |
| | 2666 | { |
| | 2667 | VERBOSE(VB_PLAYBACK, QString("Detect Letterbox: The source is not in YV12")); |
| | 2668 | return; |
| | 2669 | } |
| | 2670 | |
| | 2671 | if (sourceAspect > 15) |
| | 2672 | { |
| | 2673 | VERBOSE(VB_PLAYBACK, QString("Detect Letterbox: The source is already in widescreen")); |
| | 2674 | return; |
| | 2675 | } |
| | 2676 | |
| | 2677 | // Establish the level of light in the edge |
| | 2678 | long averageY = 0; |
| | 2679 | for (int detectionLine = 0; detectionLine < NUMBER_OF_DETECTION_LINES; detectionLine++) |
| | 2680 | { |
| | 2681 | averageY += buf[offset + 5 * pitch + xPos[detectionLine]]; |
| | 2682 | averageY += buf[offset + (height - 6) * pitch + xPos[detectionLine]]; |
| | 2683 | } |
| | 2684 | averageY /= NUMBER_OF_DETECTION_LINES * 2; |
| | 2685 | if (averageY > 64) |
| | 2686 | averageY = 0; // To bright setting to zero |
| | 2687 | |
| | 2688 | // Scan the detection lines |
| | 2689 | for (y = 5; y < height / 4; y++) // skip first pixels incase of noise in the edge |
| | 2690 | { |
| | 2691 | for (int detectionLine = 0; detectionLine < NUMBER_OF_DETECTION_LINES; detectionLine++) |
| | 2692 | { |
| | 2693 | int Y = buf[offset + y * pitch + xPos[detectionLine]]; |
| | 2694 | if ((!topHit[detectionLine]) && ( Y > averageY + THRESHOLD || Y < averageY - THRESHOLD)) |
| | 2695 | { |
| | 2696 | topHit[detectionLine] = y; |
| | 2697 | topHits++; |
| | 2698 | if (!minTop) |
| | 2699 | minTop = y; |
| | 2700 | maxTop = y; |
| | 2701 | } |
| | 2702 | |
| | 2703 | Y = buf[offset + (height - y -1) * pitch + xPos[detectionLine]]; |
| | 2704 | if ((!bottomHit[detectionLine]) && ( Y > averageY + THRESHOLD || Y < averageY - THRESHOLD)) |
| | 2705 | { |
| | 2706 | bottomHit[detectionLine] = y; |
| | 2707 | bottomHits++; |
| | 2708 | if (!minBottom) |
| | 2709 | minBottom = y; |
| | 2710 | maxBottom = y; |
| | 2711 | } |
| | 2712 | } |
| | 2713 | |
| | 2714 | if ((minTop && maxTop - minTop > HORIZONTAL_THRESHOLD) || |
| | 2715 | (minBottom && maxBottom - minBottom > HORIZONTAL_THRESHOLD)) |
| | 2716 | { |
| | 2717 | // No horizontal lines |
| | 2718 | break; |
| | 2719 | } |
| | 2720 | |
| | 2721 | if (topHits == NUMBER_OF_DETECTION_LINES && bottomHits == NUMBER_OF_DETECTION_LINES) |
| | 2722 | { |
| | 2723 | // Found the a image on all lines |
| | 2724 | if ( maxTop < halfLimit && maxBottom < halfLimit) |
| | 2725 | { |
| | 2726 | // No Letterbox |
| | 2727 | detectLetterboxCounterOff++; |
| | 2728 | detectLetterboxCounterHalf = 0; |
| | 2729 | detectLetterboxCounterFull = 0; |
| | 2730 | if (GetAdjustFill() != detectLetterboxDefaultMode) |
| | 2731 | { |
| | 2732 | VERBOSE(VB_PLAYBACK, QString("Detect Letterbox: Non Letterbox detected on line: %1 (limit: %2 counter: %3)").arg(y).arg(halfLimit).arg(detectLetterboxCounterOff)); |
| | 2733 | if (detectLetterboxCounterOff >= SWITCH_DELAY) |
| | 2734 | { |
| | 2735 | VERBOSE(VB_PLAYBACK, QString("Detect Letterbox: Switched to Fill Off")); |
| | 2736 | videoOutput->ToggleAdjustFill(detectLetterboxDefaultMode); |
| | 2737 | ReinitOSD(); |
| | 2738 | } |
| | 2739 | } |
| | 2740 | } |
| | 2741 | else if (minTop > halfLimit && minBottom > halfLimit && |
| | 2742 | maxTop < fullLimit && maxBottom < fullLimit) |
| | 2743 | { |
| | 2744 | // Letterbox (with narrow bars) |
| | 2745 | detectLetterboxCounterOff = 0; |
| | 2746 | detectLetterboxCounterHalf++; |
| | 2747 | detectLetterboxCounterFull = 0; |
| | 2748 | if (GetAdjustFill() != kAdjustFill_Half) |
| | 2749 | { |
| | 2750 | VERBOSE(VB_PLAYBACK, QString("Detect Narrow Letterbox: Letterbox detected on line: %1 (limit: %2 counter: %3)").arg(y).arg(halfLimit).arg(detectLetterboxCounterHalf)); |
| | 2751 | if (detectLetterboxCounterHalf >= SWITCH_DELAY) |
| | 2752 | { |
| | 2753 | VERBOSE(VB_PLAYBACK, QString("Detect Letterbox: Switched to Fill Half")); |
| | 2754 | videoOutput->ToggleAdjustFill(kAdjustFill_Half); |
| | 2755 | ReinitOSD(); |
| | 2756 | } |
| | 2757 | } |
| | 2758 | } |
| | 2759 | else if ( minTop > fullLimit && minBottom > fullLimit) |
| | 2760 | { |
| | 2761 | // Letterbox |
| | 2762 | detectLetterboxCounterOff = 0; |
| | 2763 | detectLetterboxCounterHalf = 0; |
| | 2764 | detectLetterboxCounterFull++; |
| | 2765 | if (GetAdjustFill() != kAdjustFill_Full) |
| | 2766 | { |
| | 2767 | VERBOSE(VB_PLAYBACK, QString("Detect Letterbox: Detected Letterbox on line: %1 (limit: %2 counter: %3)\n").arg(y).arg(fullLimit).arg(detectLetterboxCounterFull)); |
| | 2768 | if (detectLetterboxCounterFull >= SWITCH_DELAY) |
| | 2769 | { |
| | 2770 | VERBOSE(VB_PLAYBACK, QString("Detect Letterbox: Switched to Fill Full")); |
| | 2771 | videoOutput->ToggleAdjustFill(kAdjustFill_Full); |
| | 2772 | ReinitOSD(); |
| | 2773 | } |
| | 2774 | } |
| | 2775 | } |
| | 2776 | break; |
| | 2777 | } |
| | 2778 | } |
| | 2779 | |
| | 2780 | if (!isDetectLetterboxDebugging) |
| | 2781 | return; |
| | 2782 | |
| | 2783 | // draw debug output |
| | 2784 | for (x = 0; x < width; x++) |
| | 2785 | { |
| | 2786 | // detection lines |
| | 2787 | if (topHits == NUMBER_OF_DETECTION_LINES && maxTop - minTop <= HORIZONTAL_THRESHOLD) |
| | 2788 | { |
| | 2789 | // horizontal line |
| | 2790 | draw (frame, x, halfLimit, 150, 91, 62); |
| | 2791 | draw (frame, x, fullLimit, 150, 91, 62); |
| | 2792 | } |
| | 2793 | else |
| | 2794 | { |
| | 2795 | draw (frame, x, halfLimit, 255, 128, 128); |
| | 2796 | draw (frame, x, fullLimit, 255, 128, 128); |
| | 2797 | } |
| | 2798 | |
| | 2799 | if (bottomHits == NUMBER_OF_DETECTION_LINES && maxBottom - minBottom <= HORIZONTAL_THRESHOLD) |
| | 2800 | { |
| | 2801 | // horizontal line |
| | 2802 | draw (frame, x, height - 1 - halfLimit, 150, 91, 62); |
| | 2803 | draw (frame, x, height - 1 - fullLimit, 150, 91, 62); |
| | 2804 | } |
| | 2805 | else |
| | 2806 | { |
| | 2807 | draw (frame, x, height - 1 - halfLimit, 255, 128, 128); |
| | 2808 | draw (frame, x, height - 1 - fullLimit, 255, 128, 128); |
| | 2809 | } |
| | 2810 | } |
| | 2811 | |
| | 2812 | for (y = 5; y < height / 4; y++) |
| | 2813 | { |
| | 2814 | int line; |
| | 2815 | for (line = 0; line < NUMBER_OF_DETECTION_LINES; line++) |
| | 2816 | { |
| | 2817 | if (topHit[line] > y || !topHit[line]) |
| | 2818 | { |
| | 2819 | draw (frame, xPos[line], y, 150, 91, 62); |
| | 2820 | } |
| | 2821 | else |
| | 2822 | { |
| | 2823 | draw (frame, xPos[line], y, 76, 109, 207); |
| | 2824 | } |
| | 2825 | |
| | 2826 | if (bottomHit[line] > y || !bottomHit[line]) |
| | 2827 | { |
| | 2828 | draw (frame, xPos[line], height - 1 - y, 150, 91, 62); |
| | 2829 | } |
| | 2830 | else |
| | 2831 | { |
| | 2832 | draw (frame, xPos[line], height - 1 - y, 76, 109, 207); |
| | 2833 | } |
| | 2834 | } |
| | 2835 | |
| | 2836 | } |
| | 2837 | |
| | 2838 | for (x = 0; x < 25; x ++) |
| | 2839 | { |
| | 2840 | for (y = 0; y < 25; y++) |
| | 2841 | { |
| | 2842 | draw (frame, x + 30, y, averageY, x*10, y*10); |
| | 2843 | draw (frame, x, y, averageY, 128, 128); |
| | 2844 | } |
| | 2845 | } |
| | 2846 | } |
| | 2847 | |