鸿蒙(API 12 Beta3版)【AudioVivid解码】 音视频编码

获取解封装后的数据,送入解码器中,使用解码器获取PCM和Metadata元数据。

在Cmake脚本中链接到动态库

target_link_libraries(sample PUBLIC 
libnative_media_codecbase.so libnative_media_core.so 
libnative_media_acodec.so libnative_media_avdemuxer.so libnative_media_avsource.so
)

添加头文件

//解封装头文件
#include "multimedia/player_framework/native_avdemuxer.h"

// 解封装解码传递信息结构体
struct AudioSampleInfo {
std::string audioCodecMime = "";
int32_t audioSampleForamt = 0;
int32_t audioSampleRate = 0; 
int32_t audioChannelCount = 0;
int64_t audioChannelLayout = 0;
uint8_t audioCodecConfig[100] = {0};
size_t audioCodecSize = 0;
};

AudioSampleInfo  info;

定义相关实例

定义CodecBufferInfo

解码码流的属性定义,为后面传给播放的码流数据封装。

struct CodecBufferInfo {
    uint32_t bufferIndex = 0;
    uintptr_t *buffer = nullptr;
    uint8_t *bufferAddr = nullptr;
    OH_AVCodecBufferAttr attr = {0, 0, 0, AVCODEC_BUFFER_FLAGS_NONE};

    CodecBufferInfo(uint8_t *addr) : bufferAddr(addr){};
    CodecBufferInfo(uint8_t *addr, int32_t bufferSize)
        : bufferAddr(addr), attr({0, bufferSize, 0, AVCODEC_BUFFER_FLAGS_NONE}){};
    CodecBufferInfo(uint32_t argBufferIndex, OH_AVMemory *argBuffer, OH_AVCodecBufferAttr argAttr)
        : bufferIndex(argBufferIndex), buffer(reinterpret_cast<uintptr_t *>(argBuffer)), attr(argAttr){};
    CodecBufferInfo(uint32_t argBufferIndex, OH_AVMemory *argBuffer)
        : bufferIndex(argBufferIndex), buffer(reinterpret_cast<uintptr_t *>(argBuffer)){};
    CodecBufferInfo(uint32_t argBufferIndex, OH_AVBuffer *argBuffer)
        : bufferIndex(argBufferIndex), buffer(reinterpret_cast<uintptr_t *>(argBuffer)) {
        OH_AVBuffer_GetBufferAttr(argBuffer, &attr);
    };
};

定义解码工作队列

class CodecUserData {
public:
    SampleInfo *sampleInfo = nullptr;

    // 输入帧数
    uint32_t inputFrameCount_ = 0;
    // 输入队列锁,防止多线程同时操作输入队列
    std::mutex inputMutex_;
    // 输入线程的条件变量,当输入队列为空时用于阻塞输入线程
    std::condition_variable inputCond_;
    // 输入buffer队列,存放编解码器传给用户用来写入输入数据的buffer
    std::queue<CodecBufferInfo> inputBufferInfoQueue_;

    // 输出帧数
    uint32_t outputFrameCount_ = 0;
    // 输出队列锁,防止多线程同时操作输出队列
    std::mutex outputMutex_;
    // 输出线程的条件变量,当输出队列为空时用于阻塞输出线程
    std::condition_variable outputCond_;
    std::mutex renderMutex_;
    std::condition_variable renderCond_;
    // 输出buffer队列,存放编解码器传给用户用来存放输出数据的buffer
    std::queue<CodecBufferInfo> outputBufferInfoQueue_;

    std::shared_ptr<AudioDecoder> audioCodec_;
    std::queue<unsigned char> renderQueue_;

    void ClearQueue() {
        {
            std::unique_lock<std::mutex> lock(inputMutex_);
            auto emptyQueue = std::queue<CodecBufferInfo>();
            inputBufferInfoQueue_.swap(emptyQueue);
        }
        {
            std::unique_lock<std::mutex> lock(outputMutex_);
            auto emptyQueue = std::queue<CodecBufferInfo>();
            outputBufferInfoQueue_.swap(emptyQueue);
        }
    }
};

定义回调函数

class SampleCallback {
public:
    // 报错回调函数,当编解码器内部报错时调用,返回给用户相应错误码
    static void OnCodecError(OH_AVCodec *codec, int32_t errorCode, void *userData);
    // 参数修改回调函数,当编解码器参数被修改时调用,返回给用户被修改后的format参数
    static void OnCodecFormatChange(OH_AVCodec *codec, OH_AVFormat *format, void *userData);
    // 输入回调函数,当编解码器需要输入时调用,返回给用户用来写入输入数据的buffer及其对应的index
    static void OnNeedInputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData);
    // 输出回调函数,当编解码器生成新的输出数据时调用,返回给用户用来存放输出数据的buffer及其对应的index
    static void OnNewOutputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData);
};
void SampleCallback::OnCodecError(OH_AVCodec *codec, int32_t errorCode, void *userData) {
    (void)codec;
    (void)errorCode;
    (void)userData;
}
void SampleCallback::OnCodecFormatChange(OH_AVCodec *codec, OH_AVFormat *format, void *userData) {
}
void SampleCallback::OnNeedInputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData) {
    if (userData == nullptr) {
        return;
    }
    (void)codec;
    CodecUserData *codecUserData = static_cast<CodecUserData *>(userData);
    std::unique_lock<std::mutex> lock(codecUserData->inputMutex_);
    // 将输入buffer存放到输入队列中
    codecUserData->inputBufferInfoQueue_.emplace(index, buffer);
    // 通知输入线程开始运行
    codecUserData->inputCond_.notify_all();
}

void SampleCallback::OnNewOutputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData) {
    if (userData == nullptr) {
        return;
    }
    (void)codec;
    CodecUserData *codecUserData = static_cast<CodecUserData *>(userData);
    std::unique_lock<std::mutex> lock(codecUserData->outputMutex_);
    // 将输出buffer存放到输出队列中
    codecUserData->outputBufferInfoQueue_.emplace(index, buffer);
    // 通知输出线程开始运行
    codecUserData->outputCond_.notify_all();
}

开发步骤

  1. 创建解码实例。
// 创建解码器
OH_AVCodec * decoder = OH_AudioCodec_CreateByMime(info.audioCodecMime,false);

// 参数配置
OH_AVFormat *format = OH_AVFormat_Create();
OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUDIO_SAMPLE_FORMAT, SAMPLE_S16LE); //或者S24LE
OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUD_CHANNEL_COUNT, sampleInfo.audioChannelCount);
OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUD_SAMPLE_RATE, sampleInfo.audioSampleRate);
OH_AVFormat_SetIntValue(format, OH_MD_KEY_AAC_IS_ADTS, 1);
OH_AVFormat_SetLongValue(format, OH_MD_KEY_BITRATE, 96422);//码率,当前作为参考,解封装也可以获取到
OH_AVFormat_SetBuffer(format, OH_MD_KEY_CODEC_CONFIG, sampleInfo.audioCodecConfig, sampleInfo.audioCodecSize);
bool res = OH_AVFormat_SetLongValue(format, OH_MD_KEY_CHANNEL_LAYOUT, sampleInfo.audioChannelLayout);
ret = OH_AudioCodec_Configure(decoder, format);
OH_AVFormat_Destroy(format);
format = nullptr;

// 设置回调,用于输入输出buffer准备完毕后由系统回调出来
int32_t ret = OH_AudioCodec_RegisterCallback(decoder,
    {SampleCallback::OnCodecError, SampleCallback::OnCodecFormatChange,
     SampleCallback::OnNeedInputBuffer, SampleCallback::OnNewOutputBuffer},codecUserData);
// 准备回调和参数设置完毕后通知系统解码器准备好了,下一步准备启动。
ret = OH_AudioCodec_Prepare(decoder)
  1. 音频写入解码器。
int32_t PushInputData(CodecBufferInfo &info)
{
    int32_t  ret = OH_AVBuffer_SetBufferAttr(reinterpret_cast<OH_AVBuffer *>(info.buffer), &info.attr);
    ret = OH_AudioCodec_PushInputBuffer(decoder, info.bufferIndex);
    return 0;
}
  1. 释放使用过的输出码流。
int32_t AudioDecoder::FreeOutputData(uint32_t bufferIndex)
{
    int32_t ret = 0;
    ret = OH_AudioCodec_FreeOutputBuffer(decoder, bufferIndex);
    return ret ;
}
  1. 音频写入线程。
CodecUserData*audioDecContext_ = new CodecUserData;
void AudioDecInputThread()
{
    while (true) {
        if(!isStarted_){
           return;  
        }
        std::unique_lock<std::mutex> lock(audioDecContext_->inputMutex_);
        // 阻塞输入线程,直接程序运行结束,或者输入队列不为空
        bool condRet = audioDecContext_->inputCond_.wait_for(
            lock, 5s, [this]() { return !isStarted_ || !audioDecContext_->inputBufferInfoQueue_.empty(); });
        if(!isStarted_ || audioDecContext_->inputBufferInfoQueue_.empty()){
           return;  
        }
        // 获取输入buffer
        CodecBufferInfo bufferInfo = audioDecContext_->inputBufferInfoQueue_.front();
        audioDecContext_->inputBufferInfoQueue_.pop();
        audioDecContext_->inputFrameCount_++;
        lock.unlock();
        // 从解封装器中读取一帧数据写入输入buffer
        demuxer_->ReadSample(demuxer_->GetAudioTrackId(), reinterpret_cast<OH_AVBuffer *>(bufferInfo.buffer), bufferInfo.attr);
        int32_t ret = audioDecoder_->PushInputData(bufferInfo);
        if(ret != 0){
            return;
        }
        if(bufferInfo.attr.flags & AVCODEC_BUFFER_FLAGS_EOS){
            return;
        }
    }
    // StartRelease();
}
  1. 音频解码输出线程。
void AudioDecOutputThread()
{
    while (true) {
        if(!isStarted_){
           return;  
        }
        std::unique_lock<std::mutex> lock(audioDecContext_->outputMutex_);
        // 阻塞输出线程,直接程序运行结束,或者输出队列不为空
        bool condRet = audioDecContext_->outputCond_.wait_for(
            lock, 5s, [this]() { return !isStarted_ || !audioDecContext_->outputBufferInfoQueue_.empty(); });
        if(!isStarted_ || audioDecContext_->outputBufferInfoQueue_.empty()){
           return;  
        }
        // 获取输出buffer
        CodecBufferInfo bufferInfo = audioDecContext_->outputBufferInfoQueue_.front();
        audioDecContext_->outputBufferInfoQueue_.pop();
        if(bufferInfo.attr.flags & AVCODEC_BUFFER_FLAGS_EOS){
           return;  
        }
        audioDecContext_->outputFrameCount_++;
        // 获取解码后的pcm数据
        uint8_t *source = OH_AVBuffer_GetAddr(reinterpret_cast<OH_AVBuffer *>(bufferInfo.buffer));
        OH_AVFormat * format = OH_AVBuffer_GetParameter(data);
        uint8_t * metadata;
        size_t size;
        // 获取元数据
        OH_AVFormat_GetBuffer(format, OH_MD_KEY_AUDIO_VIVID_METADATA, &metadata, &size); 
#ifdef DEBUG_DECODE
        if (audioOutputFile_.is_open()) {
            audioOutputFile_.write((const char*)OH_AVBuffer_GetAddr(reinterpret_cast<OH_AVBuffer *>(bufferInfo.buffer)), bufferInfo.attr.size);
        }
#endif
        lock.unlock();
        int32_t ret = audioDecoder_->FreeOutputData(bufferInfo.bufferIndex);
        if(ret != 0){
            return;
        }
    }
}
  1. 启动解码。

    int ret = OH_AudioCodec_Start(decoder);
    
  2. 停止和释放实例。

OH_AudioCodec_Stop(decoder);
OH_AudioCodec_Destroy(decoder);
decoder = nullptr;

最后呢

很多开发朋友不知道需要学习那些鸿蒙技术?鸿蒙开发岗位需要掌握那些核心技术点?为此鸿蒙的开发学习必须要系统性的进行。

而网上有关鸿蒙的开发资料非常的少,假如你想学好鸿蒙的应用开发与系统底层开发。你可以参考这份资料,少走很多弯路,节省没必要的麻烦。由两位前阿里高级研发工程师联合打造的《鸿蒙NEXT星河版OpenHarmony开发文档》里面内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(Harmony NEXT)技术知识点

如果你是一名Android、Java、前端等等开发人员,想要转入鸿蒙方向发展。可以直接领取这份资料辅助你的学习。下面是鸿蒙开发的学习路线图。

在这里插入图片描述

针对鸿蒙成长路线打造的鸿蒙学习文档。话不多说,我们直接看详细鸿蒙(OpenHarmony )手册(共计1236页)与鸿蒙(OpenHarmony )开发入门视频,帮助大家在技术的道路上更进一步。

  • 《鸿蒙 (OpenHarmony)开发学习视频》
  • 《鸿蒙生态应用开发V2.0白皮书》
  • 《鸿蒙 (OpenHarmony)开发基础到实战手册》
  • OpenHarmony北向、南向开发环境搭建
  • 《鸿蒙开发基础》
  • 《鸿蒙开发进阶》
  • 《鸿蒙开发实战》

在这里插入图片描述

总结

鸿蒙—作为国家主力推送的国产操作系统。部分的高校已经取消了安卓课程,从而开设鸿蒙课程;企业纷纷跟进启动了鸿蒙研发。

并且鸿蒙是完全具备无与伦比的机遇和潜力的;预计到年底将有 5,000 款的应用完成原生鸿蒙开发,未来将会支持 50 万款的应用。那么这么多的应用需要开发,也就意味着需要有更多的鸿蒙人才。鸿蒙开发工程师也将会迎来爆发式的增长,学习鸿蒙势在必行! 自↓↓↓拿

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值