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

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

Запись от castaway размещена 14.11.2014 в 21:14
Показов 1862 Комментарии 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
Просмотров: 186
Размер:	9.1 Кб
ID:	2840  
Размещено в Без категории
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 2
Комментарии
  1. Старый комментарий
    Аватар для Crast
    Ужос))
    Лучше взять за основу ffmpeg с фильтрами между декодером и энкодером. Тогда можно взять любой кодек, а формат на выходе задавать строкой в фильтрах.
    Запись от Crast размещена 13.09.2015 в 09:46 Crast вне форума
  2. Старый комментарий
    Что ужасного?
    Ради одного кодека "тянуть" FFMPEG неразумно.
    Запись от castaway размещена 13.09.2015 в 14:19 castaway вне форума
 
Новые блоги и статьи
Программа опроса у.з. расходомера SLS-720F
Argus19 02.09.2026
Программа опроса у. з. расходомера SLS-720F Программа опрашивает один раз в минуту три ультразвуковых расходомера SLS-720F через интерфейс RS-485 по протоколу Modbus RTU. Опрашиваются регистры. . .
Hyper-V: Компьютер должен поддерживать доверенный платформенный модуль 2.0.
Maks 31.08.2026
При установке Windows 11 на виртуальную машину Hyper-V 2-го поколения вылезла такая ошибка: Решение: в параметрах виртуальной машины, в разделе "Безопасность" (Security) активировать флаг. . .
Архитектура биовида Стива в Майнкрафте: Зачем бонобо кубический каннибализм
anaschu 30.08.2026
Кубический Вагинокапитализм в Minecraft: Математический инвариант ОДУ и рок Стивов-бонобо Главная задача разработанной «Модели Всего» — наглядно продемонстрировать наличие системной «судьбы». . .
Оттачиваю умение писать js программы.
russiannick 30.08.2026
Проектом выходного дня стало написание Книги шифров Виженера. Итогом стала версия 200, синий туман. Синий туман назван так, потому что замораживает текст под собой. Нажатие синих кнопок управляют. . .
мат медиц модель 30. презентация проекта
anaschu 27.08.2026
хоп хоп хоп хидахоп, а я кладую))
Как у меня протекала болезнь
zorxor 27.08.2026
Здравствуйте, друзья! Эта запись блога предназначена именно для вас - для моих дорогих друзей, которые знали меня лично. Чтобы ответить на вопрос - а что же со мной произошло на самом деле? Я учился. . .
Нашел вот забавное видео о измерениях. Лучшее что я видел на эту тему
kumehtar 26.08.2026
ILETXiw9bMQ Основная суть и тезисы по измерениям: 0D (Нулевое измерение): точка, не имеющая длины, ширины, высоты или объема. Объект не может перемещаться в 0D. 1D (Первое измерение):. . .
[EasyBuilder Pro] Памятка по разработке для панелей Weintek
ФедосеевПавел 26.08.2026
Памятка по разработке для панелей Weintek ВВЕДЕНИЕ Ранее, при реализации проектов основное внимание уделял разработке управляющей программы для контроллера, а панели оператора доставалось время. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru