Skip to end of metadata
Go to start of metadata

You are viewing an old version of this content. View the current version.

Compare with Current View Version History

« Previous Version 3 Current »

FFmpegH264Encoder is to use the FFmpeg library to encode video YUV data into H264 data. On C3V we will use the encoder named h264_v4l2m2m which is a hardware encoder.

API Instructions

namespace com { namespace sunplus { namespace media {

using H264DataCallback = std::function<void(AVPacket* packet, int index)>;

class FFmpegH264Encoder {
public:
    FFmpegH264Encoder();
	~FFmpegH264Encoder();

public:
	int init(VideoStreamParam_t param, H264DataCallback dataCallback);
    void uninit();

    AVCodecContext* getAVCodecContext();

	int encode(AVFrame* yuvFrame);
    int flush();
};
}}}

Constructors

FFmpegH264Encoder();

init

Set encoding parameters, such as gop, bitrate, fps, etc, and initialize encode

/**
 * Set encoding parameters and initialize encode.
 *
 * @param param set parameters of the encoder.
 *
 * @param dataCallback to get h264 packet.
 *
 */
int init(VideoStreamParam_t param, H264DataCallback dataCallback);

Sample

VideoStreamParam_t param;
param.width = 1280;
param.height = 720;
param.pix_fmt = AV_PIX_FMT_UYVY422;
param.time_base = AV_TIME_BASE_Q;
param.gop = 30;
param.bitrate = 1000000;
param.fps = 30;

auto h264Encoder = make_shared<FFmpegH264Encoder>();

int ret = h264Encoder->init(param, [&](AVPacket* packet, int index) {
    printf("H264DataCallback, h264 frame[%d] pts: %lld, size: %d\n", index, packet->pts, packet->size);
});

uninit

Release all resources allocated by the init method and close the encoder.

/**
 * Release all resources allocated by the init method.
 */
void uninit();

getAVCodecContext

Get AVCodecContext to get the info of the encoder, such as width, height, spspps, etc.

/**
 * get encoder context.
 */
AVCodecContext* getAVCodecContext();

encode

Send the yuv frame to the encoder. the yuvFrame must be freed with av_frame_unref()+av_frame_free() when it is no longer needed.

/**
 * send the frame to the encoder.
 * the AVFrame must be freed with av_frame_unref()+av_frame_free() when
 * it is no longer needed. 
 * @return 0 if OK, < 0 on error. 
 */
int encode(AVFrame* yuvFrame);

flush

Send a NULL frame, in which case it is considered a flush packet.

If the encoder still has packets buffered, it will return them after this call.

/**
 * send a NULL frame, in which case it is considered a flush packet.
 * If the encoder still has packets buffered, it will return them after this call.
 * Once flushing mode has been entered, additional flush packets are ignored, 
 * and sending frames will return AVERROR_EOF. 
 * @return 0 if OK, < 0 on error. 
 */
int flush();

Sample Code

This is a sample of H264 encoding using FFmpegH264Encoder. FFmpegV4L2VideoSource provides the yuv source, then gets the yuv frame from FFmpegVideoProvider and sends it to the encoder.

You can also use the source code of FFmpegH264Provider as the sample code of FFmpegH264Encoder.

the flow of encode h264:

create h264 encoder --> init --> create video provider --> provider prepare --> create the thread of get frame --> send to encoder --> get h264 data by H264DataCallback.

void FFmpegH264Encoder_Test() {
    /* init output format */
    auto videoSource = make_shared<FFmpegV4L2VideoSource>("/dev/video0");
    AVDictionary *options = nullptr;
    av_dict_set(&options, "video_size", "1280x720", 0);
    av_dict_set(&options, "framerate", "30", 0);
    av_dict_set(&options, "pixel_format", "uyvy422", 0);

    /* open the device */
    int ret = videoSource->open(options);

    /* creat yuv provider */
    auto yuvProvider = yuvSource->creatVideoProvider();
    auto videoStream = yuvProvider->getAVStream();
    VideoStreamParam_t param;
    param.width = videoStream->codecpar->width;
    param.height = videoStream->codecpar->height;
    param.pix_fmt = (enum AVPixelFormat)videoStream->codecpar->format;
    param.time_base = videoStream->time_base;
    param.gop = 30;
    param.bitrate = 1000000;
    param.fps = 30;

    auto h264Encoder = make_shared<FFmpegH264Encoder>();

    int ret = h264Encoder->init(param, [&](AVPacket* packet, int index) {
        printf("H264DataCallback, h264 frame[%d] pts: %lld, size: %d\n", index, packet->pts, packet->size);
    });

    if (ret < 0) {
        h264Encoder->uninit();
        videoSource->destroyVideoProvider(yuvProvider);
        videoSource->close();
        return;
    }

    auto pCodecCtx = h264Encoder->getAVCodecContext();

    /* create the encode thread to send yuv frame to the encoder */
    auto encodeThreadFunc = [&](){
        yuvProvider->prepare();
        int yuvFrameIndex = 0;
        auto yuvFrame = av_frame_alloc();

        while(!is_exit) {
            AVPacket* yuvPacket = nullptr;
            int ret = yuvProvider->getFrame(yuvPacket);
            if (ret < 0 || yuvPacket == nullptr) {
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
                continue;            
            }
            printf("encodeThread, get yuv frame[%d] pts: %lld, size: %d\n", yuvFrameIndex, packet->pts, packet->size);

            yuvFrameIndex++;

            /* convert the yuv packet to the yuv frame */ 
            yuvFrame->width = pCodecCtx->width;
            yuvFrame->height = pCodecCtx->height;
            yuvFrame->format = pCodecCtx->pix_fmt;
            yuvFrame->pts = yuvPacket->pts;    
            av_image_fill_arrays(yuvFrame->data, yuvFrame->linesize, yuvPacket->data, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, 1);

            ret = this->h264Encoder->encode(yuvFrame);

            av_packet_unref(yuvPacket);
            av_packet_free(&yuvPacket);
            av_frame_unref(yuvFrame);
        }        

        h264Encoder->flush();
        av_frame_free(yuvFrame);
        provider->destroy();
    };

    auto thread = make_shared<thread>(encodeThreadFunc);
    
    _wait_exit("FFmpegH264Encoder_Test");
    
    if (thread->joinable()) {
        thread->join();
    }

    h264Encoder->uninit();
    videoSource->destroyVideoProvider(yuvProvider);
    videoSource->close();
}

Test Result

./ffmpeg_sample h264enc
FFmpegH264EncoderTestResult.png

  • No labels