2013-07-23 73 views
6

Tôi đang cố tạo video từ hình ảnh bằng thư viện ffmpeg. Các hình ảnh có kích thước 1920x1080 và được cho là được mã hóa với H.264 bằng cách sử dụng một thùng chứa .mkv. Tôi đã gặp nhiều vấn đề khác nhau, nghĩ rằng tôi đang tiến gần hơn đến giải pháp, nhưng điều này tôi thực sự bị mắc kẹt. Với các cài đặt tôi sử dụng, các khung hình X đầu tiên (khoảng 40, tùy thuộc vào hình ảnh và số lượng hình ảnh tôi sử dụng cho video) của video của tôi không được mã hóa. avcodec_encode_video2 không trả về bất kỳ lỗi nào (giá trị trả về là 0) với got_picture_ptr = 0. Kết quả là một video thực sự trông như mong đợi, nhưng những giây đầu tiên thì hơi khó hiểu.Tạo video từ hình ảnh bằng ffmpeg libav và libx264?

Vì vậy, đây là cách tôi tạo ra các tập tin video:

// m_codecContext is an instance variable of type AVCodecContext * 
// m_formatCtx is an instance variable of type AVFormatContext * 

// outputFileName is a valid filename ending with .mkv 
AVOutputFormat *oformat = av_guess_format(NULL, outputFileName, NULL); 
if (oformat == NULL) 
{ 
    oformat = av_guess_format("mpeg", NULL, NULL); 
} 

// oformat->video_codec is AV_CODEC_ID_H264 
AVCodec *codec = avcodec_find_encoder(oformat->video_codec); 

m_codecContext = avcodec_alloc_context3(codec); 
m_codecContext->codec_id = oformat->video_codec; 
m_codecContext->codec_type = AVMEDIA_TYPE_VIDEO; 
m_codecContext->gop_size = 30; 
m_codecContext->bit_rate = width * height * 4 
m_codecContext->width = width; 
m_codecContext->height = height; 
m_codecContext->time_base = (AVRational){1,frameRate}; 
m_codecContext->max_b_frames = 1; 
m_codecContext->pix_fmt = AV_PIX_FMT_YUV420P; 

m_formatCtx = avformat_alloc_context(); 
m_formatCtx->oformat = oformat; 
m_formatCtx->video_codec_id = oformat->video_codec; 

snprintf(m_formatCtx->filename, sizeof(m_formatCtx->filename), "%s", outputFileName); 

AVStream *videoStream = avformat_new_stream(m_formatCtx, codec); 
if(!videoStream) 
{ 
    printf("Could not allocate stream\n"); 
} 
videoStream->codec = m_codecContext; 

if(m_formatCtx->oformat->flags & AVFMT_GLOBALHEADER) 
{ 
    m_codecContext->flags |= CODEC_FLAG_GLOBAL_HEADER; 
} 

avcodec_open2(m_codecContext, codec, NULL) < 0); 
avio_open(&m_formatCtx->pb, outputFileName.toStdString().c_str(), AVIO_FLAG_WRITE); 
avformat_write_header(m_formatCtx, NULL); 

đây là cách các khung được thêm vào:

void VideoCreator::writeImageToVideo(const QSharedPointer<QImage> &img, int frameIndex) 
{ 
    AVFrame *frame = avcodec_alloc_frame(); 

    /* alloc image and output buffer */ 

    int size = m_codecContext->width * m_codecContext->height; 
    int numBytes = avpicture_get_size(m_codecContext->pix_fmt, m_codecContext->width, m_codecContext->height); 

    uint8_t *outbuf = (uint8_t *)malloc(numBytes); 
    uint8_t *picture_buf = (uint8_t *)av_malloc(numBytes); 

    int ret = av_image_fill_arrays(frame->data, frame->linesize, picture_buf, m_codecContext->pix_fmt, m_codecContext->width, m_codecContext->height, 1); 

    frame->data[0] = picture_buf; 
    frame->data[1] = frame->data[0] + size; 
    frame->data[2] = frame->data[1] + size/4; 
    frame->linesize[0] = m_codecContext->width; 
    frame->linesize[1] = m_codecContext->width/2; 
    frame->linesize[2] = m_codecContext->width/2; 

    fflush(stdout); 


    for (int y = 0; y < m_codecContext->height; y++) 
    { 
     for (int x = 0; x < m_codecContext->width; x++) 
     { 
      unsigned char b = img->bits()[(y * m_codecContext->width + x) * 4 + 0]; 
      unsigned char g = img->bits()[(y * m_codecContext->width + x) * 4 + 1]; 
      unsigned char r = img->bits()[(y * m_codecContext->width + x) * 4 + 2]; 

      unsigned char Y = (0.257 * r) + (0.504 * g) + (0.098 * b) + 16; 

      frame->data[0][y * frame->linesize[0] + x] = Y; 

      if (y % 2 == 0 && x % 2 == 0) 
      { 
       unsigned char V = (0.439 * r) - (0.368 * g) - (0.071 * b) + 128; 
       unsigned char U = -(0.148 * r) - (0.291 * g) + (0.439 * b) + 128; 

       frame->data[1][y/2 * frame->linesize[1] + x/2] = U; 
       frame->data[2][y/2 * frame->linesize[2] + x/2] = V; 
      } 
     } 
    } 

    int pts = frameIndex;//(1.0/30.0) * 90.0 * frameIndex; 

    frame->pts = pts;//av_rescale_q(m_codecContext->coded_frame->pts, m_codecContext->time_base, formatCtx->streams[0]->time_base); //(1.0/30.0) * 90.0 * frameIndex; 

    int got_packet_ptr; 
    AVPacket packet; 
    av_init_packet(&packet); 
    packet.data = outbuf; 
    packet.size = numBytes; 
    packet.stream_index = formatCtx->streams[0]->index; 
    packet.flags |= AV_PKT_FLAG_KEY; 
    packet.pts = packet.dts = pts; 
    m_codecContext->coded_frame->pts = pts; 

    ret = avcodec_encode_video2(m_codecContext, &packet, frame, &got_packet_ptr); 
    if (got_packet_ptr != 0) 
    { 
     m_codecContext->coded_frame->pts = pts; // Set the time stamp 

     if (m_codecContext->coded_frame->pts != (0x8000000000000000LL)) 
     { 
      pts = av_rescale_q(m_codecContext->coded_frame->pts, m_codecContext->time_base, formatCtx->streams[0]->time_base); 
     } 
     packet.pts = pts; 
     if(m_codecContext->coded_frame->key_frame) 
     { 
      packet.flags |= AV_PKT_FLAG_KEY; 
     } 

     std::cout << "pts: " << packet.pts << ", dts: " << packet.dts << std::endl; 

     av_interleaved_write_frame(formatCtx, &packet); 
     av_free_packet(&packet); 
    } 

    free(picture_buf); 
    free(outbuf); 
    av_free(frame); 
    printf("\n"); 
} 

và đây là dọn dẹp:

int numBytes = avpicture_get_size(m_codecContext->pix_fmt, m_codecContext->width, m_codecContext->height); 
int got_packet_ptr = 1; 

int ret; 
//  for(; got_packet_ptr != 0; i++) 
while (got_packet_ptr) 
{ 
    uint8_t *outbuf = (uint8_t *)malloc(numBytes); 

    AVPacket packet; 
    av_init_packet(&packet); 
    packet.data = outbuf; 
    packet.size = numBytes; 

    ret = avcodec_encode_video2(m_codecContext, &packet, NULL, &got_packet_ptr); 
    if (got_packet_ptr) 
    { 
     av_interleaved_write_frame(m_formatCtx, &packet); 
    } 

    av_free_packet(&packet); 
    free(outbuf); 
} 

av_write_trailer(formatCtx); 

avcodec_close(m_codecContext); 
av_free(m_codecContext); 
printf("\n"); 

Tôi cho rằng nó có thể được gắn với giá trị PTS và DTS, nhưng tôi đã thử E VERYTHING. Chỉ số khung dường như có ý nghĩa nhất. Các hình ảnh là chính xác, tôi có thể lưu chúng vào các tập tin mà không có bất kỳ vấn đề. Tôi cạn ý tưởng rồi. tôi sẽ vô cùng biết ơn nếu có ai đó ra có người biết tốt hơn tôi ...

Chúc mừng, marikaner

UPDATE:

Nếu đây là của bất kỳ sự giúp đỡ này là sản phẩm tại cuối mã hóa video:

[libx264 @ 0x7fffc00028a0] frame I:19 Avg QP:14.24 size:312420 
[libx264 @ 0x7fffc00028a0] frame P:280 Avg QP:19.16 size:148867 
[libx264 @ 0x7fffc00028a0] frame B:181 Avg QP:21.31 size: 40540 
[libx264 @ 0x7fffc00028a0] consecutive B-frames: 24.6% 75.4% 
[libx264 @ 0x7fffc00028a0] mb I I16..4: 30.9% 45.5% 23.7% 
[libx264 @ 0x7fffc00028a0] mb P I16..4: 4.7% 9.1% 4.5% P16..4: 23.5% 16.6% 12.6% 0.0% 0.0% skip:28.9% 
[libx264 @ 0x7fffc00028a0] mb B I16..4: 0.6% 0.5% 0.3% B16..8: 26.7% 11.0% 5.5% direct: 3.9% skip:51.5% L0:39.4% L1:45.0% BI:15.6% 
[libx264 @ 0x7fffc00028a0] final ratefactor: 19.21 
[libx264 @ 0x7fffc00028a0] 8x8 transform intra:48.2% inter:47.3% 
[libx264 @ 0x7fffc00028a0] coded y,uvDC,uvAC intra: 54.9% 53.1% 30.4% inter: 25.4% 13.5% 4.2% 
[libx264 @ 0x7fffc00028a0] i16 v,h,dc,p: 41% 29% 11% 19% 
[libx264 @ 0x7fffc00028a0] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 16% 26% 31% 3% 4% 3% 7% 3% 6% 
[libx264 @ 0x7fffc00028a0] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 30% 26% 14% 4% 5% 4% 7% 4% 7% 
[libx264 @ 0x7fffc00028a0] i8c dc,h,v,p: 58% 26% 13% 3% 
[libx264 @ 0x7fffc00028a0] Weighted P-Frames: Y:17.1% UV:3.6% 
[libx264 @ 0x7fffc00028a0] ref P L0: 63.1% 21.4% 11.4% 4.1% 0.1%  
[libx264 @ 0x7fffc00028a0] ref B L0: 85.7% 14.3% 
[libx264 @ 0x7fffc00028a0] kb/s:27478.30 

Trả lời

2

Libav có thể trì hoãn việc xử lý khung ban đầu. Một phương pháp hay là kiểm tra các khung bị trì hoãn sau khi bạn xử lý xong tất cả các khung. Điều này được thực hiện như sau:

int i=NUMBER_OF_FRAMES_PREVIOUSLY_ENCODED 
for(; got_packet_ptr; i++) 
    ret = avcodec_encode_video2(m_codecContext, &packet, NULL, &got_packet_ptr); 
//Write the packets to a container after this. 

Điểm là chuyển con trỏ NULL vào vị trí của khung được mã hóa và tiếp tục làm như vậy cho đến khi gói bạn nhận được không trống. Xem this link để biết ví dụ về mã - phần nằm trong "tải khung bị trì hoãn".

Một cách dễ dàng hơn ra sẽ được thiết lập số lượng khung hình b là 0.

m_codecContext->max_b_frames = 0; 

Hãy cho tôi biết nếu điều này hoạt động tốt.

Ngoài ra, bạn chưa sử dụng API libx264 chút nào. Bạn có thể sử dụng các API libx264 để mã hóa video, chúng có cú pháp đơn giản và rõ ràng hơn. Thêm vào đó, nó cung cấp cho bạn nhiều quyền kiểm soát hơn đối với cài đặt và hiệu suất được cải thiện.

Để ghi luồng video vào vùng chứa mkv, bạn vẫn sẽ phải sử dụng thư viện libav. Tuy nhiên.

+0

Cảm ơn bạn rất nhiều vì đã dành thời gian. Thật không may là không thiết lập số lượng khung hình b cũng như viết các khung bị trì hoãn có vẻ như là lừa. Mặc dù rõ ràng là các khung bị trì hoãn, khi chương trình đi vào vòng lặp. Video thực sự có vẻ ít tăng vọt nhưng vẫn không chính xác. Có vẻ như có một lỗ sau 2 giây chỉ có 2 hình ảnh tĩnh được hiển thị trong 2 giây như thể các khung ở giữa bị thiếu. – marikaner

+0

Bạn có thể chỉ định số lượng khung hình trong video kết quả và tổng số Hình ảnh bạn dự định mã hóa không? Bạn chỉ có thể kiểm tra số lượng cuộc gọi đến av_interleaved_write() (nên là 480 theo bản cập nhật của bạn). Ngoài ra, tính toán frameIndex là gì? –

+0

Có, số lượng hình ảnh tôi đang thụt lề để mã hóa là 480. frameIndex chỉ là một số nguyên được tăng từ 0 đến 479 với mỗi khung hình. av_interleaved_write() được gọi là 442 lần sau khi avcodec_encode_video2 sử dụng một khung thực tế và 38 lần sau avcodec_encode_video2 bằng cách sử dụng NULL. – marikaner

Các vấn đề liên quan