------------------------------------全系列文章目录------------------------------------
大致操作流程如下
检测歌曲文件是否存在封面
-
AVStream结构体中的disposition参数,但该参数为AV_DISPOSITION_ATTACHED_PIC标志位时,表明存在封面图片。
-
ffmpeg中对disposition参数的部分解释:
demuxing: set by libavformat when creating the stream or in avformat_find_stream_info().
-
ffmpeg中对AV_DISPOSITION_ATTACHED_PIC标志位解释如下
The stream is stored in the file as an attached picture/“cover art” (e.g.APIC frame in ID3v2).The first (usually only) packet associated with it will be returned among the first few packets read from the file unless seeking takes place. It can also be accessed at any time in AVStream.attached_pic.
-
因此当我们通过video stream的disposition参数判断是否有封面图片,然后从AVStream.attached_pic中去获取该封面图片。
封面图片解码操作
- 从上文可知我们需要对AVStream.attached_pic数据包进行解码操作,需要注意的是若封面文件格式为png,则因为png数据格式原因,需要进行多次解码操作。
图片格式转换
-
在创建纹理时需要指定输入数据的像素格式,同时该像素格式应为渲染器所支持。
-
当封面图片的像素格式不符合预期时,需要将其转换为创建纹理时指定的像素格式,转换操作依赖于SwsContext结构体。该结构体用于对图像数据进行处理,如裁切、放大以及格式转换等。
-
对于封面图片的像素格式可以从对应编码器结构体AVCodecContext的pix_fmt获取。
/*创建纹理*/ SDL_CreateTexture(pRender, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, pixel_w, pixel_h); /*创建SwsContext*/ sws_getContext(src_w, src_h, codec_ctx->pix_fmt, src_w, src_h, out_pix_fmt, SWS_BICUBIC, NULL, NULL, NULL); /*对原数据进行格式转换和裁切处理*/ sws_scale(img_convert_ctx, pf->data, pf->linesize, 0, src_h, pf_YUV->data, pf_YUV->linesize);
调整封面图片大小
-
封面图片大小的调整依赖于SDL事件,通过捕获SDL_WINDOWEVENT事件(当窗口变化时触发);在该事件中提取SDL_WINDOWEVENT_SIZE_CHANGED事件类型(窗口大小发生变化时触发),获取当前的窗口大小,更新渲染输出窗口的大小;最后用原图像数据再次进行一次渲染操作。
SDL_PumpEvents(); while (1) { while (!SDL_PeepEvents(&_event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT)) { SDL_PumpEvents(); } switch (_event.type) { case SDL_WINDOWEVENT: switch (_event.window.event) { case SDL_WINDOWEVENT_SIZE_CHANGED: SDL_LockMutex(vc->dis.mutex); vc->dis.set_screen_wh(_event.window.data1, _event.window.data2); vc->dis.set_out_ret_wh(); SDL_UnlockMutex(vc->dis.mutex); vc->dis.display_frame(vc->deal.pf_YUV); break; default: break; } break; default: break; } }
/*显示一帧图像*/ void display::display_frame(AVFrame *pF) { SDL_LockMutex(mutex); SDL_UpdateTexture(pTexture, NULL, pF->data[0], pF->linesize[0]); //更新整个纹理图像 SDL_RenderClear(pRender); //清除渲染目标,接收下一帧 SDL_RenderCopy(pRender, pTexture, NULL, &out_ret); //复制纹理图像至渲染器,选择整个纹理作为输入,选择渲染目标某块区域作为输出 SDL_RenderPresent(pRender); //使用渲染结果更新屏幕 SDL_UnlockMutex(mutex); }