基于rkmedia的视频解码

将ts流flv流mp4流视频数据解码

        这里main文件主要是执行了rkmedia_decoder_init 与decode_threads_init两个工作,其中第一个工作是初始化vdec模块(video decode)

//这是rv1126对视频数据进行解码的文件
#include <stdio.h>
// #include "video_queue.h"
using namespace std;
#include "rkmedia_module_config.h"
#include "video_queue.h"
video_queue *video_queue1 = new video_queue(); 
extern int rkmedia_decoder_init();
extern void decode_threads_init();
int main(int argc, char *argv[])
{


    rkmedia_decoder_init();
    decode_threads_init();
    while (1)
    {
        /* code */
    }
    
    // while(1)
    // {

    // }

    return 0;
}

视频解码模块初始化如下:

RK_MPI_SYS_Init是初始化rkmdia模块之前必须要使用的

配置了解码模块H264、软件解码、帧格式的过程(通常来说我们选用帧解码而非流解码)

(在这一步时候我调试发现一直出现解码模块初始化失败的报错,后来发现是文件中其他的库可能放的不够或者放错了,编译是没问题的执行时一直报错VDEC Create失败)

int rkmedia_decoder_init()
{
    int ret;
    RK_MPI_SYS_Init();
    VDEC_CHN_ATTR_S vdec_attrs;
    vdec_attrs.enCodecType    = RK_CODEC_TYPE_H264;       //解码数据格式 H264
    vdec_attrs.enDecodecMode  = VIDEO_DECODEC_SOFTWARE;   //解码模式:软件解码 硬件解码
    vdec_attrs.enMode         = VIDEO_MODE_FRAME;         //数据格式 帧格式 流格式

    ret = RK_MPI_VDEC_CreateChn(0,&vdec_attrs);
    if(ret)
    {
        printf("creat chnnel err!\r\n");
        printf("Create Vdec[0] failed! ret=%d\n", ret);

        return -1;
    }
    printf("vdec creat chnnel success!\r\n");


    return 0;
}

创建线程模块主要是创建了三个线程:

1.读取本地被编码的数据read_video_thread并存入队列

2.将队列内的数据转化为rkmedia能读懂的形式并输出到VDEC

3.从VDEC取出解码数据显示

#include<stdio.h>
#include<pthread.h>
pthread_t read_video_pid;
pthread_t send_video_decode_pid;
pthread_t display_video_pid;

extern void *read_video_thread(void *arg); 
extern void *send_video_decode_thread(void *arg); 
extern void *display_video_thread(void *arg); 

void decode_threads_init()
{
    //读取本地编码后的视频文件到队列
    pthread_create(&read_video_pid,NULL,read_video_thread,NULL);
    // //取出队列中的编码后的视频文件送往解码器
    pthread_create(&send_video_decode_pid,NULL,send_video_decode_thread,NULL);
 
    // //显示解码后的数据
    pthread_create(&display_video_pid,NULL,display_video_thread,NULL);

    printf("decode threads creat success!\r\n");
}

read_video_thread:这个主要是调用了ffmpeg库来读取编码的数据

这里我总结了6步,打开文件、获取文件信息、获取流、获取编码器、获取编码器上下文并绑定、开始读取,这里其实与编码部分类似


#include <stdio.h>
//确保ffmpeg_include.h正确使用的预处理宏
#define __STDC_CONSTANT_MACROS
#define TS_FILE "video.ts"
// #include "include/ffmpeg_include.h"
#include "video_queue.h"
#include <stdlib.h>
#include<pthread.h>

#define __STDC_CONSTANT_MACROS
extern "C"
{
#include <libavutil/avassert.h>
#include <libavutil/channel_layout.h>
#include <libavutil/opt.h>
#include <libavutil/mathematics.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include "libavutil/time.h"
}
extern video_queue* video_queue1;

/*
    1.打开文件
    2.获取文件信息
    3.获取对应流
    4.获取对应编码器
    5.分配编码器上下文并绑定编码器
    6.开始读取
*/
void *read_video_thread(void *arg) {
    pthread_detach(pthread_self());
    printf("----------------read_video_thread-------------\r\n");

    AVFormatContext *avfmt_context = NULL;
    //打开媒体文件并绑定信息到媒体上下文
    int ret = avformat_open_input(&avfmt_context, TS_FILE, NULL, NULL);
    if (ret < 0) {
        printf("avformat_open_input error\r\n");
        return NULL;
    }
    //寻找对应流文件
    avformat_find_stream_info(avfmt_context,nullptr); 
    int numret = av_find_best_stream(avfmt_context,AVMEDIA_TYPE_VIDEO,-1,-1,nullptr,0);
    if(numret <0)
    {
       printf("find best stream err!\r\n");
       avformat_free_context(avfmt_context);
       return NULL;
 
    }  
    AVCodec *avcode = avcodec_find_decoder(avfmt_context->streams[numret]->codecpar->codec_id);
    AVCodecContext *avcodecontext = avcodec_alloc_context3(avcode);
    ret = avcodec_parameters_to_context(avcodecontext,avfmt_context->streams[numret]->codecpar);
    if(ret < 0)
    {
        avcodec_free_context(&avcodecontext);
        printf("get code context err!\r\n");
        return NULL;
    } 
    AVPacket *packet = av_packet_alloc();

    while(av_read_frame(avfmt_context,packet) >= 0)
    {
        if(packet->stream_index == numret)
        {
           video_data_packet_t *video_packet = (video_data_packet_t *)malloc(sizeof(video_data_packet_t));
           memcpy(video_packet->videobuff,packet->data,packet->size);
           video_packet->size = packet->size;
           video_queue1 ->putVideoPacketQueue(video_packet);
        }
    }
    // 释放资源
    if (avfmt_context) 
    {
        avformat_close_input(&avfmt_context);
    }

    return NULL;
}


将队列内packet转化为rkmedia能识别的数据:

rkmedia会读取MEDIA_BUFFER类型的数据实质也就是void *,因此我们先分配了这样的MEDIA_BUFFER数据(这里可以理解MEDIA_BUFFER是一个画布,需要指定其框架大小后往里面填充数据),然后把我们的数据拷进去,大小也考进去,发送到VDEC最后释放

#include <stdio.h>
#include <cstdlib>
#include<pthread.h>
#include <stdlib.h>
#include "video_queue.h"
#include "rkmedia_buffer.h"
#include <string.h>
#include <rkmedia_api.h>

extern video_queue* video_queue1;
//为什么要把mp4的packet放入队列再取出送到decoder?
//因为mp4的packet是普通的数据,而rv1126解码器需要读取特定类型的数据
/*
    1.取出数据
    2.创建画布
    3.复制信息
    4.发送数据
    5.释放信息
 */
void *send_video_decode_thread(void *arg)
{
    free(arg);
    pthread_detach(pthread_self());
    MB_IMAGE_INFO_S image_info = {1920,1080,1920,1080,IMAGE_TYPE_NV12};
    MEDIA_BUFFER mb = NULL;
    // avformat_open_input();
    while(1)
    {
        video_data_packet_t *video_packet = video_queue1->getVideoPacketQueue();
        //MEDIA_BUFFER就是视频模板 属性 是否硬件buffer 硬件buffer是否有缓冲区
        mb = RK_MPI_MB_CreateImageBuffer(&image_info,RK_TRUE,MB_FLAG_NOCACHED);
        memcpy(RK_MPI_MB_GetPtr(mb),video_packet->videobuff,video_packet->size);
        printf("size if %d\r\n",video_packet->size);
        RK_MPI_MB_SetSize(mb,video_packet->size);
        RK_MPI_SYS_SendMediaBuffer(RK_ID_VDEC,0,mb);
        RK_MPI_MB_ReleaseBuffer(mb);

    }
    return NULL;
}

显示模块,我没屏幕,没做,但是你可以建立一个线程然后RK_MPI_SYS_GetMediaBuffer得到数据然后RK_MPI_MB_GetImageInfo获取信息,打印出来看这个信息判断是否正常运行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值