1. H264 编解码

1.1 结构体

1
2
3
AVCodec//编码器结构体
AVCodecContext//编码器上下文
AVFrame//解码后的帧

1.2 方法

1
2
3
av_frame_alloc()/av_frame_free()//结构体分配与释放
avcodec_alloc_context3()//分配上下文
avcodec_free_context()//释放上下文

1.3 解码步骤

查找解码器

1
avcodec_find_coder()

打开解码器

1
avcodec_open2()

解码

1
avcodec_decode_video2()

1.4 编码步骤

查找编码器

1
avcodec_find_encoder_by_name()

设置编码参数,打开编码器

1
avcodec_open2()

编码

1
avcodec_encode_video2()

1.4 实例

初始化

 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
    ...
    //注册
    avcodec_register_all();
    ...
    // codec_name = libx264 
    codec = avcodec_find_encoder_by_name(codec_name);    
    ...
    // 获得AVCodecContext
    c = avcodec_alloc_context3(codec);
    ...
    // 参数
    c->bit_rate = 400000;//码率
    c->width = 352;//宽度
    c->height = 288;//高度
    c->time_base = (AVRational){1, 25};//时间基
    c->framerate = (AVRational){25, 1};//帧率
    c->gop_size = 10;//GOP 大小
    c->max_b_frames = 1;//b帧大小
    c->pix_fmt = AV_PIX_FMT_YUV420P;//存储格式
    ...
    // 打开
    avcodec_open2(c, codec, NULL);
    ...
    // 分配frame 变量
    frame = av_frame_alloc();
    ...
    frame->format = c->pix_fmt;
    frame->width  = c->width;
    frame->height = c->height;
    ret = av_frame_get_buffer(frame, 32);
    ...

编码

 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
    for (i = 0; i < 25; i++) {
        av_init_packet(&pkt);
        pkt.data = NULL;    // packet data will be allocated by the encoder
        pkt.size = 0;
        fflush(stdout);
        /* make sure the frame data is writable */
        ret = av_frame_make_writable(frame);
        if (ret < 0)
            exit(1);
        /* prepare a dummy image */
        /* Y */
        for (y = 0; y < c->height; y++) {
            for (x = 0; x < c->width; x++) {
                frame->data[0][y * frame->linesize[0] + x] = x + y + i * 3;
            }
        }
        /* Cb and Cr */
        for (y = 0; y < c->height/2; y++) {
            for (x = 0; x < c->width/2; x++) {
                frame->data[1][y * frame->linesize[1] + x] = 128 + y + i * 2;
                frame->data[2][y * frame->linesize[2] + x] = 64 + x + i * 5;
            }
        }
        frame->pts = i;
        /* encode the image */
        ret = avcodec_encode_video2(c, &pkt, frame, &got_output);
        if (ret < 0) {
            fprintf(stderr, "Error encoding frame\n");
            exit(1);
        }

        if (got_output) {
            printf("Write frame %3d (size=%5d)\n", i, pkt.size);
            fwrite(pkt.data, 1, pkt.size, f);
            av_packet_unref(&pkt);
        }
    }
     /* get the delayed frames */
    for (got_output = 1; got_output; i++) {
        fflush(stdout);

        ret = avcodec_encode_video2(c, &pkt, NULL, &got_output);
        if (ret < 0) {
            fprintf(stderr, "Error encoding frame\n");
            exit(1);
        }

        if (got_output) {
            printf("Write frame %3d (size=%5d)\n", i, pkt.size);
            fwrite(pkt.data, 1, pkt.size, f);
            av_packet_unref(&pkt);
        }
    }
    /* add sequence end code to have a real MPEG file */
    fwrite(endcode, 1, sizeof(endcode), f);
    fclose(f);
    avcodec_free_context(&c);
    av_frame_free(&frame);

2. AAC 编码

2.1 编码方法

1
2
3
4
5
6
//编码器寻找方法
avcodec_find_encoder_by_name();
//上下文
avcodec_alloc_context3();
//编码
avcodec_encodec_audio2();

2.2 实例

 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
    ...
    avcodec_register_all();
    ...
    codec = avcodec_find_encoder(AV_CODEC_ID_MP2);
    ...
    c = avcodec_alloc_context3(codec);
    ...
    c->bit_rate = 64000;
    c->sample_fmt = AV_SAMPLE_FMT_S16;
    c->sample_rate    = select_sample_rate(codec);
    c->channel_layout = select_channel_layout(codec);
    c->channels       = av_get_channel_layout_nb_channels(c->channel_layout);
    ...
     ret = av_frame_get_buffer(frame, 0);
    ...
    t = 0;
    tincr = 2 * M_PI * 440.0 / c->sample_rate;
    for (i = 0; i < 200; i++) {
        av_init_packet(&pkt);
        pkt.data = NULL; // packet data will be allocated by the encoder
        pkt.size = 0;
        /* make sure the frame is writable -- makes a copy if the encoder
         * kept a reference internally */
        ret = av_frame_make_writable(frame);
        if (ret < 0)
            exit(1);
        samples = (uint16_t*)frame->data[0];

        for (j = 0; j < c->frame_size; j++) {
            samples[2*j] = (int)(sin(t) * 10000);

            for (k = 1; k < c->channels; k++)
                samples[2*j + k] = samples[2*j];
            t += tincr;
        }
        /* encode the samples */
        ret = avcodec_encode_audio2(c, &pkt, frame, &got_output);
        if (ret < 0) {
            fprintf(stderr, "Error encoding audio frame\n");
            exit(1);
        }
        if (got_output) {
            fwrite(pkt.data, 1, pkt.size, f);
            av_packet_unref(&pkt);
        }
    }
    for (got_output = 1; got_output; i++) {
        ret = avcodec_encode_audio2(c, &pkt, NULL, &got_output);
        if (ret < 0) {
            fprintf(stderr, "Error encoding frame\n");
            exit(1);
        }

        if (got_output) {
            fwrite(pkt.data, 1, pkt.size, f);
            av_packet_unref(&pkt);
        }
    }
    fclose(f);
    av_frame_free(&frame);
    avcodec_free_context(&c);

3. 视频转图片

3.1 获取AVFrame

 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
static int decode_write_frame(const char *outfilename, AVCodecContext *avctx,
                              struct SwsContext *img_convert_ctx, AVFrame *frame, int *frame_count, AVPacket *pkt, int last)
{
    int len, got_frame;
    char buf[1024];

    len = avcodec_decode_video2(avctx, frame, &got_frame, pkt);
    if (len < 0) {
        fprintf(stderr, "Error while decoding frame %d\n", *frame_count);
        return len;
    }
    if (got_frame) {
        printf("Saving %sframe %3d\n", last ? "last " : "", *frame_count);
        fflush(stdout);
        snprintf(buf, sizeof(buf), "%s-%d.bmp", outfilename, *frame_count);
        saveBMP(img_convert_ctx, frame, buf);

        (*frame_count)++;
    }
    if (pkt->data) {
        pkt->size -= len;
        pkt->data += len;
    }
    return 0;
}

3.2 存储格式转化

1
2
3
4
5
6
7
8
9
    //YUV->RGB
    int w = frame->width;
    int h = frame->height;
    int numBytes=avpicture_get_size(AV_PIX_FMT_BGR24, w, h);
    uint8_t *buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
    AVFrame *pFrameRGB = av_frame_alloc();
    avpicture_fill((AVPicture *)pFrameRGB, buffer, AV_PIX_FMT_BGR24, w, h);
    sws_scale(img_convert_ctx, frame->data, frame->linesize,
              0, h, pFrameRGB->data, pFrameRGB->linesize);

3.3 生成图片

 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
    //2 构造 BITMAPINFOHEADER
    BITMAPINFOHEADER header;
    header.biSize = sizeof(BITMAPINFOHEADER);

    header.biWidth = w;
    header.biHeight = h*(-1);
    header.biBitCount = 24;
    header.biCompression = 0;
    header.biSizeImage = 0;
    header.biClrImportant = 0;
    header.biClrUsed = 0;
    header.biXPelsPerMeter = 0;
    header.biYPelsPerMeter = 0;
    header.biPlanes = 1;

    //3 构造文件头
    BITMAPFILEHEADER bmpFileHeader = {0,};
    //HANDLE hFile = NULL;
    DWORD dwTotalWriten = 0;
    DWORD dwWriten;

    bmpFileHeader.bfType = 0x4d42; //'BM';
    bmpFileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)+ numBytes;
    bmpFileHeader.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);

    FILE* pf = fopen(filename, "wb");
    fwrite(&bmpFileHeader, sizeof(BITMAPFILEHEADER), 1, pf);
    fwrite(&header, sizeof(BITMAPINFOHEADER), 1, pf);
    fwrite(pFrameRGB->data[0], 1, numBytes, pf);

4.SDL 开发

4,1 使用步骤

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
//1.添加头文件
#include <SDL.h>
//2.初始化
SDL_Init();
//创建窗口
SDL_Window();
//创建渲染器
SDL_CreateRender();
//清除
SDL_RenderClear();
//推送
SDL_RenderPresent();
//销毁渲染器
SDL_DestoryRenderer();
//销毁窗口
SDL_DestroyWindow();
//3.退出
SDL_Quit();

4.2 实例

 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
#include <SDL.h>
#include <stdio.h>
int main(int argc,char* argv[]){
    SDL_Window *window = NULL;
    SDL_Init(SDL_INIT_VIDEO);
    window = SDL_CreateWindow("SDL2 Window",200,200,640,480,SDL_WINDOW_SHOWN );
    if(!window){
        printf("failed to create window");
        goto _EXIT;
    }
    SDL_Renderer *render = SDL_CreateRenderer(window,-1,0);
    if(!render){
        SDL_Log("failed to create render");
        goto _DWINDOW;
    }
    SDL_SetRenderDrawColor(render,255,0,0,255);
    SDL_RenderClear(render);
    SDL_RenderPresent(render);
    SDL_Event event;
    while(1){
      if (SDL_PollEvent(&event)) {
          printf("event is %#x\n", event.type); // test code
          if (SDL_QUIT == event.type) {
             break;
          }
      }
    }
_DWINDOW:
    SDL_DestroyWindow(window);
_EXIT:
    SDL_Quit();
    return 0;
}
1
clang -g -o [bin] *.c `pkg-config --cflags --libs sdl2`

5. SDL 事件

SDL将所有的事件都存放在一个队列中,所有对事件的操作,其实就是对队列的操作

SDL 事件分类

1
2
3
SDL_WindowEvent:窗口事件
SDL_KeyboardEvent:键盘事件
SDL_MouseMotionEvent:鼠标事件

5.1 方法

1
2
3
4
5
6
//取出事件
SDL_PollEvent();
//等待事件
SDL_WaitEvent();
//等待指定时间长度
SDL_WaitTimeOut();

6. 纹理渲染

纹理渲染

6.1 方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
//纹理创建
SDL_CreateTexture(format,access);
    format:YUV,RGB
    access:Texture类型TargetStream
//销毁
SDL_DestroyTexture();
//纹理设置
SDL_SetRenderTarget();
//清除
SDL_RenderClear();
//拷贝数据到纹理
SDL_RenderCopy();
//推送纹理
SDL_RenderPresent();

6.2 实例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
SDL_Rect rect;
rect.w = 30;
rect.h = 30;
rect.x = rand()%600;
rect.y = rand()%480;
//设置背景为黑色
SDL_SetRenderTarget(render,texture);
SDL_SetRenderDrawColor(render,0,0,0,0);
SDL_RenderClear(render);
//渲染rect
SDL_RenderDrawRect(render,&rect);
SDL_SetRenderDrawColor(render,255,0,0,0);
SDL_RenderFillRect(render,&rect);
//数据复制
SDL_SetRenderTarget(render,NULL);
SDL_RenderCopy(render,texture,NULL,NULL);
//显示
SDL_RenderPresent(render);

7.YUV播放

7.1 线程创建

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
//创建
SDL_CreateThread(fn,name);
    fn:执行的方法名
    name:线程名字
    data:参数
//等待
SDL_WaitThead();

//创建锁 销毁锁
SDL_CreateMutex/SDL_destroyMutex
//上锁 开锁
SDL_LockMutex/SDL_UnlockMutex

//创建条件/销毁条件
SDL_CreateCond/SDL_DestroyCond
//等待条件/发送信号
SDL_CondWait/SDL_CondSignal

入队

 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
 int packet_queue_put(PacketQueue *q, AVPacket *pkt) {

  AVPacketList *pkt1;
  if(av_dup_packet(pkt) < 0) {
    return -1;
  }
  pkt1 = av_malloc(sizeof(AVPacketList));
  if (!pkt1)
    return -1;
  pkt1->pkt = *pkt;
  pkt1->next = NULL;

  SDL_LockMutex(q->mutex);

  if (!q->last_pkt) {
    q->first_pkt = pkt1;
  }else{
    q->last_pkt->next = pkt1;
  }

  q->last_pkt = pkt1;
  q->nb_packets++;
  q->size += pkt1->pkt.size;
  SDL_CondSignal(q->cond);

  SDL_UnlockMutex(q->mutex);
  return 0;
}

出队

 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
int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block)
{
  AVPacketList *pkt1;
  int ret;

  SDL_LockMutex(q->mutex);

  for(;;) {

    if(quit) {
      ret = -1;
      break;
    }

    pkt1 = q->first_pkt;
    if (pkt1) {
      q->first_pkt = pkt1->next;
      if (!q->first_pkt)
    q->last_pkt = NULL;
      q->nb_packets--;
      q->size -= pkt1->pkt.size;
      *pkt = pkt1->pkt;
      av_free(pkt1);
      ret = 1;
      break;
    } else if (!block) {
      ret = 0;
      break;
    } else {
      SDL_CondWait(q->cond, q->mutex);
    }
  }
  SDL_UnlockMutex(q->mutex);
  return ret;
}

7.2 更新纹理

1
2
SDL_UpdateTexture();
SDL_UpdateYUVTexture();

7.3 实例

 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
/**单独线程产生渲染频率**/
int refresh_video_timer(void *udata){

    thread_exit=0;

    while (!thread_exit) {
        SDL_Event event;
        event.type = REFRESH_EVENT;
        SDL_PushEvent(&event);
        SDL_Delay(40);
    }

    thread_exit=0;

    //push quit event
    SDL_Event event;
    event.type = QUIT_EVENT;
    SDL_PushEvent(&event);

    return 0;
}
    ...
int main(int argc, char* argv[]){
    ...
    /**开启频率线程**/
    timer_thread = SDL_CreateThread(refresh_video_timer,
                                    NULL,
                                    NULL);
    /**持续渲染texture***/
    do {
        //Wait
        SDL_WaitEvent(&event);
        if(event.type==REFRESH_EVENT){
            ...
            SDL_UpdateTexture( texture, NULL, video_pos, video_width);
            //FIX: If window is resize
            rect.x = 0;
            rect.y = 0;
            rect.w = w_width;
            rect.h = w_height;
            SDL_RenderClear( renderer );
            SDL_RenderCopy( renderer, texture, NULL, &rect);
            SDL_RenderPresent( renderer );
            ...
        }else if(event.type==SDL_WINDOWEVENT){
            //If Resize
            SDL_GetWindowSize(win, &w_width, &w_height);
        }else if(event.type==SDL_QUIT){
            thread_exit=1;
        }else if(event.type==QUIT_EVENT){
            break;
        }
    }while ( 1 );
}   

8. SDL 播放音频

8.1 方法

1
2
3
4
5
6
//打开音频设备
SDL_OpenAudio()/SDL_CloseAudio()
//播放与暂停
SDL_PauseAudio()
//混音
SDL_MixAudio()

8.2 实例

初始化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
...
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER);
...
audio_fd = fopen(path, "r");
...
//SDL_AudioSpec
spec.freq = 44100;;
spec.format = AUDIO_S16SYS;
spec.channels = 2;
spec.silence = 0;
spec.samples = 2048;;
spec.callback = read_audio_data;;
spec.userdata = NULL;
...
SDL_OpenAudio(&spec, NULL);
...
SDL_PauseAudio(0);

读取数据

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
do{
    //read data from pcm file
    buffer_len = fread(audio_buf, 1, BLOCK_SIZE, audio_fd);
    fprintf(stderr, "block size is %zu\n", buffer_len);

    audio_pos = audio_buf;

    //the main thread wait for a moment
    while(audio_pos < (audio_buf + buffer_len)) {
        SDL_Delay(1);
    }
}while(buffer_len !=0);
SDL_CloseAudio();

消耗数据

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
//SDL回调使用,消耗audio_pos所指的数据 一秒25帧
void read_audio_data(void *udata, Uint8 *stream, int len){

    if(buffer_len == 0){
        return;
    }

    SDL_memset(stream, 0, len);

    len = (len < buffer_len) ? len : buffer_len;
    SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);

    audio_pos += len;
    buffer_len -= len;
}