| 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 | |