(オーディオとビデオの研究ノート):FFmpegメモリモデルとAVPacketとAVFrameの一般的なAPI

目次

FFmpegメモリモデル

FFmpegメモリモデル-参照カウント

AVPacket共通API

AVFrame共通API

APIアプリケーションの紹介

FFmpegメモリモデル

  • av_read_frameからAVPacketを読み取った後、それをデコーダーキューに入れる方法は?
  • avcodec_recevice_frameからAVFrameを読み取った後、それを解凍されたフレームキューに入れる方法は?
     
  • 既存のパケットから新しいパケットをコピーする場合、次の2つの状況があります
    • ①2つのパケットのbufsが同じデータキャッシュスペースを参照しています。このとき、データキャッシュスペースの解放に注意する必要があります。
    • ②2つの参照のパケットは異なるデータバッファスペースをbufせず、それぞれにコピーパケットデータキャッシュスペースがあります。

  • メモリモデル

  • より正確なモデル

FFmpegメモリモデル-参照カウント

  • 同じキャッシュスペースを共有する複数のAVPacketの場合、FFmpegで使用される参照カウントメカニズム(参照カウント):
    • 参照カウントは0に初期化されますが、AVBufferが実際に割り当てられた場合にのみ、参照カウントは1に初期化されます。
    • ときに、新たなパケットが共有キャッシュ領域を参照し、参照カウントは1だけ増加させ
    • 場合は、共有スペースが解放された参考文献、そのパケットは、参照カウントは-1
    • 参照カウントが0の場合、参照されたバッファスペースAVBufferが解放されます
  • AVFrameも同じメカニズムを使用します。

AVPacket共通API

AVPacket * av_packet _alloc(void); 現時点では、AVPacketの割り当てはバッファとは関係ありません。
void av_packet _free(AVPacket ** pkt); AVPacketと_allocの対応を解放します
void av_init_packet(AVPacket * pkt); AVPacketの初期化は、単にpktフィールドを初期化することです。
int av_new_packet(AVPacket * pkt、int size); AVPacketのbufにメモリを割り当て、参照カウントを1に初期化します。
int av_packet_ ref(AVPacket * dst、const AVPacket * src); 参照カウントを増やす
void av_packet_ unref(AVPacket * pkt); 参照カウントを減らします
void av_packet_ move_ref(AVPacket * dst、AVPacket * src); 転送参照カウント
AVPacket * av_packet_clone(const AVPacket * src); 等式av_packet_alloc()+ av_packet_ref(

AVFrame共通API

AVFrame * av_frame_ alloc(void); AVFrameを割り当てます
void av_frame_ free(AVFrame ** frame); AVFrameをリリース
int av_frame_ ref(AVFrame * dst、const AVFrame * src); 参照カウントを増やす
void av_frame_ unref(AVFrame * frame); 参照カウントを減らします
void av_frame_ move_ref(AVFrame * dst、AVFrame * src); 転送参照カウント
int av_frame_get_buffer(AVFrame * frame、int align); AVFrameに従ってメモリを割り当てます
AVFrame * av_frame_clone(const AVFrame * src); 等式av_frame_alloc()+ av_frame_ref()

APIアプリケーションの紹介

  • av_packet_allocは、単にAVPacket作成し、そのフィールドをデフォルト値に設定し(データは空で、データバッファースペースはありません)、データポインターに別の値を割り当てる必要があります
AVPacket *av_packet_alloc(void);
int av_new_packet(AVPacket *pkt, int size);
  • av_packet_allocで作成されたAVPacketを 解放し ます。パケットに参照カウントがある場合(packet- > bufが空でない場合)、最初にav_packet_unref(&packet)を呼び出します
    • 参照カウントが0の場合にのみav_packet_free()呼び出されたときにデータキャッシュ解放さます。
void av_packet_free(AVPacket **pkt);
  • av_init_packetは、パケット値をデフォルト値に初期化します。この関数は、データバッファーのスペースとバッファーによって参照されるサイズに影響を与えないため、個別に処理する必要があります。
    • AVPacketのbufは空です。
    • たとえば、av_get_packetでav_init_packetを呼び出します
void av_init_packet(AVPacket *pkt);
  • av_packet_refは、参照カウントの浅いコピーを使用します
    • この関数は、最初にキャッシュされていないすべてのデータをコピーし、次にsrc-> bufの新しい参照カウントを作成します。
    • srcがすでに参照カウントを設定している場合(src-> bufが空ではない場合)、その参照カウントを直接1に追加します。
    • srcが参照カウントを設定しない場合(src-> bufが空の場合)、dstの新しい参照カウントbufを作成し、src-> dataをbuf-> bufferにコピーします。
    • 最後に、srcの他のフィールドをdstにコピーします。したがって、av_packet_ref()は2つのAVPacketを使用してキャッシュを共有します。
int av_packet_ref(AVPacket *dst, const AVPacket *src);
  • av_packet_unrefは、参照カウントを使用してデータをクリーンアップします
    • キャッシュスペースの参照カウントを-1に設定し、パケットの他のフィールドを初期値に設定します。
    • 参照カウントが0の場合、キャッシュスペースは自動的に解放されます。
void av_packet_unref(AVPacket *pkt);
  • av_packet_move_ref は、src構造全体をdstに直接割り当てるため、参照カウントは変更されず、srcはav_init_packetによってリセットされます。
    • その関数はav_packet_alloc()+ av_packet_ref();です。
void av_packet_move_ref(AVPacket *dst, AVPacket *src);
  • av_packet_cloneは、最初に新しいAVPacketを作成し、次に参照+データコピーをカウントして、新しいAVPacketが古いAVPacketの同じデータを指すようにします。
AVPacket *av_packet_clone(const AVPacket *src);
  • AVPacketケース:
/**
 * @brief 测试av_packet_alloc和av_packet_free的配对使用
 */
void av_packet_test1()
{
    AVPacket *pkt = NULL;
    int ret = 0;

    pkt = av_packet_alloc();
    ret = av_new_packet(pkt, MEM_ITEM_SIZE); // 引用计数初始化为1
    memccpy(pkt->data, (void *)&av_packet_test1, 1, MEM_ITEM_SIZE);
    av_packet_unref(pkt);       // 要不要调用
    av_packet_free(&pkt);       // 如果不free将会发生内存泄漏,内部调用了 av_packet_unref
}

/**
 * @brief 测试误用av_init_packet将会导致内存泄漏
 */
void av_packet_test2()
{
    AVPacket *pkt = NULL;
    int ret = 0;

    pkt = av_packet_alloc();
    ret = av_new_packet(pkt, MEM_ITEM_SIZE);
    memccpy(pkt->data, (void *)&av_packet_test1, 1, MEM_ITEM_SIZE);
//    av_init_packet(pkt);        // 这个时候init就会导致内存无法释放
    av_packet_free(&pkt);
}

/**
 * @brief 测试av_packet_move_ref后,可以av_init_packet
 */
void av_packet_test3()
{
    AVPacket *pkt = NULL;
    AVPacket *pkt2 = NULL;
    int ret = 0;

    pkt = av_packet_alloc();
    ret = av_new_packet(pkt, MEM_ITEM_SIZE);
    memccpy(pkt->data, (void *)&av_packet_test1, 1, MEM_ITEM_SIZE);
    pkt2 = av_packet_alloc();   // 必须先alloc
    av_packet_move_ref(pkt2, pkt);//内部其实也调用了av_init_packet
    av_init_packet(pkt);
    av_packet_free(&pkt);
    av_packet_free(&pkt2);
}
/**
 * @brief 测试av_packet_clone
 */
void av_packet_test4()
{
    AVPacket *pkt = NULL;
    // av_packet_alloc()没有必要,因为av_packet_clone内部有调用 av_packet_alloc
    AVPacket *pkt2 = NULL;
    int ret = 0;

    pkt = av_packet_alloc();
    ret = av_new_packet(pkt, MEM_ITEM_SIZE);
    memccpy(pkt->data, (void *)&av_packet_test1, 1, MEM_ITEM_SIZE);
    pkt2 = av_packet_clone(pkt); // av_packet_alloc()+av_packet_ref()
    av_init_packet(pkt);
    av_packet_free(&pkt);
    av_packet_free(&pkt2);
}

/**
 * @brief 测试av_packet_ref
 */
void av_packet_test5()
{
    AVPacket *pkt = NULL;
    AVPacket *pkt2 = NULL;
    int ret = 0;

    pkt = av_packet_alloc(); //
    if(pkt->buf)        // 打印referenc-counted,必须保证传入的是有效指针
    {    printf("%s(%d) ref_count(pkt) = %d\n", __FUNCTION__, __LINE__,
               av_buffer_get_ref_count(pkt->buf));
    }

    ret = av_new_packet(pkt, MEM_ITEM_SIZE);
    if(pkt->buf)        // 打印referenc-counted,必须保证传入的是有效指针
    {    printf("%s(%d) ref_count(pkt) = %d\n", __FUNCTION__, __LINE__,
               av_buffer_get_ref_count(pkt->buf));
    }
    memccpy(pkt->data, (void *)&av_packet_test1, 1, MEM_ITEM_SIZE);

    pkt2 = av_packet_alloc();   // 必须先alloc
    av_packet_move_ref(pkt2, pkt); // av_packet_move_ref
//    av_init_packet(pkt);  //av_packet_move_ref

    av_packet_ref(pkt, pkt2);
    av_packet_ref(pkt, pkt2);     // 多次ref如果没有对应多次unref将会内存泄漏
    if(pkt->buf)        // 打印referenc-counted,必须保证传入的是有效指针
    {    printf("%s(%d) ref_count(pkt) = %d\n", __FUNCTION__, __LINE__,
               av_buffer_get_ref_count(pkt->buf));
    }
    if(pkt2->buf)        // 打印referenc-counted,必须保证传入的是有效指针
    {    printf("%s(%d) ref_count(pkt) = %d\n", __FUNCTION__, __LINE__,
               av_buffer_get_ref_count(pkt2->buf));
    }
    av_packet_unref(pkt);   // 将为2
    av_packet_unref(pkt);   // 做第二次是没有用的
    if(pkt->buf)
        printf("pkt->buf没有被置NULL\n");
    else
        printf("pkt->buf已经被置NULL\n");
    if(pkt2->buf)        // 打印referenc-counted,必须保证传入的是有效指针
    {    printf("%s(%d) ref_count(pkt) = %d\n", __FUNCTION__, __LINE__,
               av_buffer_get_ref_count(pkt2->buf));
    }
    av_packet_unref(pkt2);


    av_packet_free(&pkt);
    av_packet_free(&pkt2);
}

/**
 * @brief 测试AVPacket整个结构体赋值, 和av_packet_move_ref类似
 */
void av_packet_test6()
{
    AVPacket *pkt = NULL;
    AVPacket *pkt2 = NULL;
    int ret = 0;

    pkt = av_packet_alloc();
    ret = av_new_packet(pkt, MEM_ITEM_SIZE);
    memccpy(pkt->data, (void *)&av_packet_test1, 1, MEM_ITEM_SIZE);

    pkt2 = av_packet_alloc();   // 必须先alloc
    *pkt2 = *pkt;   // 有点类似  pkt可以重新分配内存
    av_init_packet(pkt);

    av_packet_free(&pkt);
    av_packet_free(&pkt2);
}
  • AVFrameケーステスト
void av_frame_test()
{
    AVFrame *frame = NULL;
    int ret = 0;

    frame = av_frame_alloc();// 没有类似的AVPacket的av_new_packet的API
    // 1024 *2 * (16/8) =
    frame->nb_samples     = 1024;
    frame->format         = AV_SAMPLE_FMT_S16;//AV_SAMPLE_FMT_S16P AV_SAMPLE_FMT_S16
    frame->channel_layout = AV_CH_LAYOUT_STEREO;    //AV_CH_LAYOUT_MONO AV_CH_LAYOUT_STEREO
    ret = av_frame_get_buffer(frame, 0);    // 根据格式分配内存
    if(frame->buf && frame->buf[0])
        printf("%s(%d) 1 frame->buf[0]->size = %d\n", __FUNCTION__, __LINE__, frame->buf[0]->size);    //受frame->format等参数影响
    if(frame->buf && frame->buf[1])
        printf("%s(%d) 1 frame->buf[1]->size = %d\n", __FUNCTION__, __LINE__, frame->buf[1]->size);    //受frame->format等参数影响

    if(frame->buf && frame->buf[0])        // 打印referenc-counted,必须保证传入的是有效指针
        printf("%s(%d) ref_count1(frame) = %d\n", __FUNCTION__, __LINE__, av_buffer_get_ref_count(frame->buf[0]));

    ret = av_frame_make_writable(frame);    // 当frame本身为空时不能make writable
    printf("av_frame_make_writable ret = %d\n", ret);

    if(frame->buf && frame->buf[0])        // 打印referenc-counted,必须保证传入的是有效指针
        printf("%s(%d) ref_count2(frame) = %d\n", __FUNCTION__, __LINE__, av_buffer_get_ref_count(frame->buf[0]));

    av_frame_unref(frame);
    if(frame->buf && frame->buf[0])        // 打印referenc-counted,必须保证传入的是有效指针
        printf("%s(%d) ref_count3(frame) = %d\n", __FUNCTION__, __LINE__, av_buffer_get_ref_count(frame->buf[0]));

    av_frame_free(&frame);
}

 

おすすめ

転載: blog.csdn.net/baidu_41388533/article/details/112736236