ffmpeg編程(一)


這篇主要講如何以路徑的方式打開一個視頻

int main() {
 AVFormatContext *pFormatCtx;
 int i, videoStream;
 AVCodecContext *pCodecCtx;
 AVCodec *pCodec;
 AVFrame *pFrame;
 AVFrame *pFrameRGB;
 AVPacket packet;
 int frameFinished = NULL;
 int numBytes;
 uint8_t *buffer;
 struct SwsContext *pSwsCtx;

AVFormatContext是輸入輸出信息的容器,需要注意的是其中兩個成員:

 struct AVInputFormat * AVIFormat;  //數據輸入格式
 struct AVOutputFormat * AVIFormat; //數據輸出格式

這兩個成員不能同時賦值,簡單來說 AVFormatContext 不能作為輸入容器又作為輸出容器。

i是保存幀的, videoStream 找到一個視頻流並且記錄該數組的坐標
AVCodecContext動態的記錄一個解碼器的上下文信息

AVCodec:ffmpeg中編解碼器是由鏈表結構管理的,鏈表的第一個節點是在文件libavcodec/util.c中聲明的:

static AVCodec *first_avcodec = NULL;

對於編碼器、與解碼器的操作都是在圍繞AVCodec該鏈表執行的。

AVFrame用來保存數據緩存的對像

AVPacket主要記錄音視頻數據幀,時鍾信息和壓縮數據首地址,大小等信息。

SwsContext視頻分辯率、色彩空間變換時所需要的上下文句柄。

av_register_all();
 //視頻的路徑
 const char *filename = "/root/Desktop/test.flv";
 if (av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL) != 0)
  return -1; // Couldn't open file
 if (av_find_stream_info(pFormatCtx) < 0)
  return -1; // Couldn't find stream information

 // Dump information about file onto standard error
 dump_format(pFormatCtx, 0, filename, 0);

av_register_all();函數通過執行libvcodec/allcodes.c文件里的avcodec_register_all()來初始化所有encoder/decoder。

av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL)函數讀取文件的頭部並且把信息保存到我們給的AVFormatContext結構體中。最后三個參數用來指定特殊的文件格式,緩沖大小和格式參數,但如果把它們設置為空NULL或者0,libavformat將自動檢測這些參數。

av_open_input_file 只是檢測了文件的頭部,所以接着 av_find_stream_info(pFormatCtx) 函數負責檢查在文件中的流的信息。

那么頭文件和文件中的流信息都沒有問題的話,接下來dump_format(pFormatCtx, 0, filename, 0) 函數負責為pFormatCtx->streams填充上正確的信息。
 videoStream = -1;
 for (i = 0; i < pFormatCtx->nb_streams; i++)
  if (pFormatCtx->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
   videoStream = i;
   break;
  }
 if (videoStream == -1)
  return -1; // Didn't find a video stream

現在pFormatCtx->streams僅僅是一組大小為pFormatCtx->nb_streams的指針,所以讓我們先跳過它直到我們找到一個視頻流並且記錄該數組的坐標。

pCodecCtx = pFormatCtx->streams[videoStream]->codec;

得到編解碼器上下文信息。

  pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    if (pCodec == NULL) {
        fprintf(stderr, "Unsupported codec!\n");
        return -1; // Codec not found
    }

得到了編碼器的上下文信息之后,但是AVCodecContext 包含了流中所使用的關於編解碼器的所有信息,現在我們有了一個指向他的指針。所以必需要找到真正的編解碼器avcodec_find_decoder(pCodecCtx->codec_id)函數就是做的這件事情。

    if (avcodec_open(pCodecCtx, pCodec) < 0)
        return -1; // Could not open codec

avcodec_open(pCodecCtx, pCodec)函數負責打開pCodec解碼器。

 // Allocate video frame
    pFrame = avcodec_alloc_frame();

    // Allocate an AVFrame structure
    pFrameRGB = avcodec_alloc_frame();
    if (pFrameRGB == NULL)
        return -1;

avcodec_alloc_frame()函數負責申請內存,注意:初始化的時候AVFrame中的元素data,linesize均為空。未指向任何內存數據

    // Determine required buffer size and allocate buffer
    numBytes = avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,
            pCodecCtx->height);
    buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));

    // Assign appropriate parts of buffer to image planes in pFrameRGB
    // Note that pFrameRGB is an AVFrame, but AVFrame is a superset
    // of AVPicture
    avpicture_fill((AVPicture *) pFrameRGB, buffer, PIX_FMT_RGB24,
    pCodecCtx->width, pCodecCtx->height);

函數avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,pCodecCtx->height);負責計算AVCodecContext緩沖區的大小和申請空間。

函數av_malloc(numBytes * sizeof(uint8_t))申請內存對齊(但不保證內存泄漏)。

函數avpicture_fill((AVPicture *) pFrameRGB, buffer, PIX_FMT_RGB24,pCodecCtx->width, pCodecCtx->height);把幀和pFrameRGB關聯起來。

  // Read frames and save first five frames to disk
    i = 0;
    while (av_read_frame(pFormatCtx, &packet) >= 0) {
        // Is this a packet from the video stream?
        if (packet.stream_index == videoStream) {

            // Allocate video frame
            pFrame = avcodec_alloc_frame();
            int w = pCodecCtx->width;
            int h = pCodecCtx->height;
            // Decode video frame
            avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
            pSwsCtx = sws_getContext(w, h, pCodecCtx->pix_fmt, w, h,
                    PIX_FMT_RGB565, SWS_POINT, NULL, NULL, NULL);
            // Did we get a video frame?

            if (frameFinished) {
                // Convert the image from its native format to RGB
                sws_scale(pSwsCtx, pFrame->data, pFrame->linesize, 0,
                        pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
                // Save the frame to disk

                ++i;
                printf("%d\n", i);
                SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i);
            }
        }

函數av_read_frame(pFormatCtx, &packet)讀取一個包並且把它保存到AVPacket結構體中。

函數avcodec_decode_video2()把包轉換為幀。

函數sws_getContext(w, h, pCodecCtx->pix_fmt, w, h, PIX_FMT_RGB565, SWS_POINT, NULL, NULL, NULL);負責得到視頻分辯率、色彩空間變換時所需要的上下文句柄。

函數sws_scale(pSwsCtx, pFrame->data, pFrame->linesize, 0,pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);把RGB格式轉換成image。

void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) {
    FILE *pFile;
    const char *szFilename = "/root/Desktop/ffmpeg";
    int y;

    // Open file
    pFile = fopen(szFilename, "wt");
    if (pFile == NULL)
        return;

    // Write header
    //printf("P6\n%d %d\n255\n", width, height);
    printf("bbbbbbbbbb\n");
    // Write pixel data
    for (y = 0; y < height; y++)
        fwrite(pFrame->data[0] + y * pFrame->linesize[0], sizeof(char),
                width * 3, pFile);

    // Close file
    fclose(pFile);
} 

把圖片保存成文件,這里就不再敘述了。

// Free the packet that was allocated by av_read_frame
        av_free_packet(&packet);
    }

    // Free the RGB image
    av_free(buffer);
    av_free(pFrameRGB);

    // Free the YUV frame
    av_free(pFrame);

    // Close the codec
    avcodec_close(pCodecCtx);

    // Close the video file
    av_close_input_file(pFormatCtx);

    return 0;
}

釋放內存。

c文件下載

http://download.csdn.net/detail/wenwei19861106/4219809


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM