Форум программистов, компьютерный форум, киберфорум
castaway
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  

libx265 - кодируем видео в самом современном формате

Запись от castaway размещена 14.11.2014 в 21:14
Показов 1836 Комментарии 2

В данной статье я опишу простой пример использования библиотеки libx265, которая является реализацией нового стандарта кодирования видео H265/HEVC, пришедшему на замену "относительно" старого H264.
Я приведу простейший пример использования библиотеки libx265, остальные возможности останутся "за кадром".

Сразу оговорюсь - комментариев в коде мало. Я не стал ими разбрасываться и увеличивать размер примера, т.к. на самом деле там всё очень просто и понятно.
Достаточно иметь при себе небольшие знания английского языка и немного логики. К тому же, все непонятные ситуации можно обсудить в комментариях.

Итак, я реализовал простой класс для работы с кодировщиком, который имеет четыре метода вместе с конструктором и деструктором.
В конструкторе мы задаём параметры потока и callback-функцию для вывода кодированной информации.
Метод bool is_valid() позволяет узнать, успешно ли был инициализирован кодировщик. Я специально не использую исключения, не спрашивайте меня почему. ...хотя, если хотите то спросите.
Метод bool encode( uint8_t * pY, uint8_t * pU, uint8_t * pV ) производит кодирование 3-х планов - Y, U и V, которые передаются в метод в виде указателей на данные. Как раз он и вызывает нашу callback-функцию.

Особенности реализации:
* в классе жёстко забит формат входящей картинки I420
* в классе жёстко забита глубина цвета 8 бит
* в классе жёстко забит контроль качества CRF (Constant Rate Factor), который варьируется от 0 до 1 включительно, где 0 - лучшее качество, 1 - худшее. Это значение указывается в конструкторе класса.
* в классе жёстко забиты предустановки: preset - "normal", tune - "zerolatency"
* разрешение видео задаётся в конструкторе
* FPS задаётся в конструкторе


** следующий код не претендует на звание "самого лучшего враппера", т.к. был написан целиком и полностью за вчерашний день. Обработка ошибок так же не на самом высоком уровне.

В качестве примера я кодирую изображение чёрно-белой шахматной доски, которая двигается вверх и влево:
- размер клеток - 16 пикселей
- размер изображения - 640x480 пикселей
- показатель качества - 0.5
- количество кадров - 100

Внимание!
Выходной файл (out.raw) записывается "сырым", т.е. без контейнера, только заголовок формата видео, после чего идут закодированные фреймы.
В данный момент выходной файл я могу просмотреть только через FFMPEG, который успешно его читает и показывает. Ссылка на предкомпилированный FFMPEG для ОС Windows находится в конце статьи.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#include <cstring>
#include <cstdio>
#include "x265.h"
 
class x265_encoder {
    typedef bool (*write_callback_t)( int action, const x265_nal *, uint32_t );
 
    bool                m_is_valid;
    x265_encoder *      m_encoder;
    x265_picture *      m_picture;
    unsigned            m_frame;
    write_callback_t    m_write_callback;
 
public:
    enum { OPEN, WRITE, CLOSE };
 
    x265_encoder( unsigned width, unsigned height, unsigned fps, double quality, write_callback_t write_callback )
        : m_is_valid( false ), m_frame( 0 ), m_write_callback( write_callback ) {
 
        x265_param * p_param = x265_param_alloc();
        x265_param_default( p_param );
 
        // presets: "ultrafast", "superfast", "veryfast", "faster", "fast", "medium", "slow", "slower", "veryslow", "placebo"
        if ( x265_param_default_preset( p_param, "medium", "zerolatency" ) < 0 ) {
            fprintf( stderr, "x265_param_default_preset(): error\n" );
        } else {
            p_param->sourceWidth = width;
            p_param->sourceHeight = height;
 
            p_param->fpsNum = fps;
            p_param->fpsDenom = 1;
 
            p_param->rc.rateControlMode = X265_RC_CRF;
 
            p_param->rc.rfConstant = (quality < 0 ? 0 : (quality > 1 ? 1 : (quality * 51)));
 
            m_encoder = x265_encoder_open( p_param );
            if ( !m_encoder ) {
                fprintf( stderr, "x265_encoder_open(): error\n" );
            } else {
                x265_encoder_parameters( m_encoder, p_param );
 
                m_write_callback( OPEN, 0, 0 );
 
                if ( !p_param->bRepeatHeaders ) {
                    x265_nal *  p_nal;
                    uint32_t    n_nal;
                    x265_encoder_headers( m_encoder, &p_nal, &n_nal );
                    m_write_callback( WRITE, p_nal, n_nal );
                }
 
                m_picture = x265_picture_alloc();
                x265_picture_init( p_param, m_picture );
                m_picture->colorSpace = X265_CSP_I420;
                m_picture->bitDepth = 8;
 
                // I420
                m_picture->stride[0] = p_param->sourceWidth;
                m_picture->stride[1] = p_param->sourceWidth / 2;
                m_picture->stride[2] = p_param->sourceWidth / 2;
 
                m_is_valid = true;
            }
        }
        x265_param_free( p_param );
    }
 
    virtual ~x265_encoder() {
        if ( is_valid() ) {
            m_write_callback( CLOSE, 0, 0 );
            x265_picture_free( m_picture );
            x265_encoder_close( m_encoder );
        }
    }
 
    bool is_valid() const { return m_is_valid; }
 
    bool encode( uint8_t * pY, uint8_t * pU, uint8_t * pV ) {
        m_picture->pts = m_frame;
        m_picture->planes[0] = pY;
        m_picture->planes[1] = pU;
        m_picture->planes[2] = pV;
 
        x265_nal *  p_nal;
        uint32_t    n_nal;
        int err = x265_encoder_encode( m_encoder, &p_nal, &n_nal, m_picture, NULL );
        if ( err < 0 ) return false;
        m_write_callback( WRITE, p_nal, n_nal );
        ++m_frame;
        return true;
    }
};
 
static bool write_cb( int action, const x265_nal * p_nal, uint32_t n_nal ) {
    static FILE * fp = NULL;
    switch ( action ) {
        case x265_encoder::OPEN:
            fp = fopen( "out.raw", "wb+" );
            if ( !fp ) return false;
            break;
 
        case x265_encoder::CLOSE:
            fclose( fp );
            break;
 
        case x265_encoder::WRITE:
            for ( unsigned i = 0; i < n_nal; ++i ) {
                fwrite( p_nal->payload, p_nal->sizeBytes, 1, fp );
                p_nal++;
            }
            break;
    }
    return true;
}
 
int main()
{
    const int w = 320;
    const int h = 240;
 
    x265_encoder enc( w, h, 30, .5, write_cb );
    if ( enc.is_valid() ) {
 
        uint8_t * pY = new uint8_t [w * h];
        uint8_t * pU = new uint8_t [w / 2 * h / 2];
        uint8_t * pV = new uint8_t [w / 2 * h / 2];
 
        // set U and V planes to 128
        memset( pU, 128, w / 2 * h / 2 );
        memset( pV, 128, w / 2 * h / 2 );
 
        for ( unsigned i = 0; i < 100; ++i ) {
 
            // set Y plane
            for ( int y = 0; y < h; y++ )
            for ( int x = 0; x < w; x++ ) {
                pY[y * w + x] = (((x + i) >> 4) + ((y + i) >> 4)) & 1 ? 255 : 0;
            }
 
            enc.encode( pY, pU, pV );
        }
 
        delete [] pV;
        delete [] pU;
        delete [] pY;
    }
}
libx265 - https://bitbucket.org/multicoreware/x265/
Zeranoe's FFmpeg Builds Home Page - http://ffmpeg.zeranoe.com/builds/
Миниатюры
Нажмите на изображение для увеличения
Название: libx265_wrapper_test.png
Просмотров: 180
Размер:	9.1 Кб
ID:	2840  
Размещено в Без категории
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 2
Комментарии
  1. Старый комментарий
    Аватар для Crast
    Ужос))
    Лучше взять за основу ffmpeg с фильтрами между декодером и энкодером. Тогда можно взять любой кодек, а формат на выходе задавать строкой в фильтрах.
    Запись от Crast размещена 13.09.2015 в 09:46 Crast вне форума
  2. Старый комментарий
    Что ужасного?
    Ради одного кодека "тянуть" FFMPEG неразумно.
    Запись от castaway размещена 13.09.2015 в 14:19 castaway вне форума
 
Новые блоги и статьи
Часы электронные
Uhbif79 12.08.2026
Выкладываю программу часов. Программа позволяет: 1. Использовать системное время и дату, 2. Есть возможность вводить время и дату вручную. 3. Реализованы 2 будильника: начало и конец рабочего дня. . . .
Часы с будильником на основе класса QLCDNumber
Uhbif79 12.08.2026
Всем добрый день, выкладываю программу часов с будильником на основе класса QLCDNumber. Здесь я пробовал самостоятельно создавал классы, впервые столкнулся с видимостью переменной одного класса из. . .
Установка MinGW GCC 16.2 и CMake
8Observer8 10.08.2026
VK Видео: https:/ / vkvideo. ru/ video-240781534_456239017 YouTube: eY5-5PyI9NM Текстовая версия
Неделя из жизни имитационной модели склада: мои кривые руки растут, откуда надо
anaschu 10.08.2026
Неделя из жизни имитационной модели склада: как я почти написал неправильную логику и что с этим делать Работаю сейчас над учебно-рабочим проектом: строю в AnyLogic имитационную модель процессов. . .
Калькулятор для расчета родства
russiannick 07.08.2026
1. Задача: Создать калькулятор для расчета родства. Родственных связей существует 8 ступеней, такие как: p - отец P - мать q - муж Q - жена b - брат B - сестра s - сын S - дочь
Мир по моей воле
kumehtar 07.08.2026
Когда-то кажется, что всё просто. Ты весь такой светлый. Причиняешь добро. Борешься за справедливость в этом тёмном мире. Потом начинаешь замечать одну неприятную вещь. Почти каждый хороший. . .
Кредитный калькулятор
Maks 05.08.2026
Решение задачи по прикладной информатике средствами 1С. Задача: Напишите приложение-калькулятор, которое помогает рассчитывать параметры кредита для аннуитетного и дифференцированного видов. . .
У нас сейчас поговорку "Опять 25" нужно переделать на "Опять +35".
kumehtar 04.08.2026
С ностальгией вспоминаю времена моего детства, когда у нас и правда +25 - была максимальная температура летом. Раньше +25 °C реально казались вершиной жары, когда можно было весь день пропадать на. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru