1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chromecast/media/cma/test/frame_segmenter_for_test.h"
10 #include "base/callback.h"
11 #include "base/logging.h"
12 #include "base/run_loop.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "chromecast/media/cma/base/decoder_buffer_adapter.h"
15 #include "media/base/decoder_buffer.h"
16 #include "media/base/demuxer.h"
17 #include "media/base/media_log.h"
18 #include "media/base/test_helpers.h"
19 #include "media/filters/ffmpeg_demuxer.h"
20 #include "media/filters/file_data_source.h"
21 #include "media/filters/h264_parser.h"
23 namespace chromecast
{
28 struct AudioFrameHeader
{
31 int sampling_frequency
;
35 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 };
36 int mp3_sample_rate
[] = { 44100, 48000, 32000, 0 };
38 AudioFrameHeader
FindNextMp3Header(const uint8
* data
, size_t data_size
) {
40 AudioFrameHeader header
;
41 header
.frame_size
= 0;
45 for (size_t k
= 0; k
< data_size
- 4 && !found
; k
++) {
47 // syncword: 11111111111
50 if (!(data
[k
+ 0] == 0xff && (data
[k
+ 1] & 0xfe) == 0xfa))
53 int bitrate_index
= (data
[k
+ 2] >> 4);
54 if (bitrate_index
== 0 || bitrate_index
== 15) {
55 // Free size or bad bitrate => not supported.
59 int sample_rate_index
= (data
[k
+ 2] >> 2) & 0x3;
60 if (sample_rate_index
== 3)
64 ((1152 / 8) * mp3_bitrate
[bitrate_index
] * 1000) /
65 mp3_sample_rate
[sample_rate_index
];
66 if (data
[k
+ 2] & 0x2)
69 // Make sure the frame is complete.
70 if (k
+ frame_size
> data_size
)
73 if (k
+ frame_size
< data_size
- 3 &&
74 !(data
[k
+ frame_size
+ 0] == 0xff &&
75 (data
[k
+ frame_size
+ 1] & 0xfe) == 0xfa)) {
81 header
.frame_size
= frame_size
;
82 header
.sampling_frequency
= mp3_sample_rate
[sample_rate_index
];
89 BufferList
Mp3SegmenterForTest(const uint8
* data
, size_t data_size
) {
91 BufferList audio_frames
;
92 base::TimeDelta timestamp
;
95 AudioFrameHeader header
= FindNextMp3Header(&data
[offset
],
97 if (header
.frame_size
== 0)
100 header
.offset
+= offset
;
101 offset
= header
.offset
+ header
.frame_size
;
103 scoped_refptr
< ::media::DecoderBuffer
> buffer(
104 ::media::DecoderBuffer::CopyFrom(
105 &data
[header
.offset
], header
.frame_size
));
106 buffer
->set_timestamp(timestamp
);
107 audio_frames
.push_back(
108 scoped_refptr
<DecoderBufferBase
>(new DecoderBufferAdapter(buffer
)));
110 // 1152 samples in an MP3 frame.
111 timestamp
+= base::TimeDelta::FromMicroseconds(
112 (UINT64_C(1152) * 1000 * 1000) / header
.sampling_frequency
);
117 struct H264AccessUnit
{
126 H264AccessUnit::H264AccessUnit()
133 BufferList
H264SegmenterForTest(const uint8
* data
, size_t data_size
) {
134 BufferList video_frames
;
135 std::list
<H264AccessUnit
> access_unit_list
;
136 H264AccessUnit access_unit
;
138 int prev_pic_order_cnt_lsb
= 0;
139 int pic_order_cnt_msb
= 0;
141 scoped_ptr
< ::media::H264Parser
> h264_parser(new ::media::H264Parser());
142 h264_parser
->SetStream(data
, data_size
);
146 ::media::H264NALU nalu
;
147 switch (h264_parser
->AdvanceToNextNALU(&nalu
)) {
148 case ::media::H264Parser::kOk
:
150 case ::media::H264Parser::kInvalidStream
:
151 case ::media::H264Parser::kUnsupportedStream
:
153 case ::media::H264Parser::kEOStream
:
160 // To get the NALU syncword offset, substract 3 or 4
161 // which corresponds to the possible syncword lengths.
162 size_t nalu_offset
= nalu
.data
- data
;
164 if (nalu_offset
> 0 && data
[nalu_offset
-1] == 0)
167 switch (nalu
.nal_unit_type
) {
168 case ::media::H264NALU::kAUD
: {
171 case ::media::H264NALU::kSPS
: {
173 if (h264_parser
->ParseSPS(&sps_id
) != ::media::H264Parser::kOk
)
175 if (access_unit
.has_vcl
) {
176 access_unit
.size
= nalu_offset
- access_unit
.offset
;
177 access_unit_list
.push_back(access_unit
);
178 access_unit
= H264AccessUnit();
179 access_unit
.offset
= nalu_offset
;
183 case ::media::H264NALU::kPPS
: {
185 if (h264_parser
->ParsePPS(&pps_id
) != ::media::H264Parser::kOk
)
187 if (access_unit
.has_vcl
) {
188 access_unit
.size
= nalu_offset
- access_unit
.offset
;
189 access_unit_list
.push_back(access_unit
);
190 access_unit
= H264AccessUnit();
191 access_unit
.offset
= nalu_offset
;
195 case ::media::H264NALU::kIDRSlice
:
196 case ::media::H264NALU::kNonIDRSlice
: {
197 ::media::H264SliceHeader shdr
;
198 if (h264_parser
->ParseSliceHeader(nalu
, &shdr
) !=
199 ::media::H264Parser::kOk
) {
202 const ::media::H264PPS
* pps
=
203 h264_parser
->GetPPS(shdr
.pic_parameter_set_id
);
206 const ::media::H264SPS
* sps
=
207 h264_parser
->GetSPS(pps
->seq_parameter_set_id
);
209 // Very simplified way to segment H264.
210 // This assumes only 1 VCL NALU per access unit.
211 if (access_unit
.has_vcl
) {
212 access_unit
.size
= nalu_offset
- access_unit
.offset
;
213 access_unit_list
.push_back(access_unit
);
214 access_unit
= H264AccessUnit();
215 access_unit
.offset
= nalu_offset
;
218 access_unit
.has_vcl
= true;
220 // Support only explicit POC so far.
221 if (sps
->pic_order_cnt_type
!= 0) {
222 LOG(WARNING
) << "Unsupported pic_order_cnt_type";
225 int diff_pic_order_cnt_lsb
=
226 shdr
.pic_order_cnt_lsb
- prev_pic_order_cnt_lsb
;
227 int max_pic_order_cnt_lsb
=
228 1 << (sps
->log2_max_pic_order_cnt_lsb_minus4
+ 4);
229 if (diff_pic_order_cnt_lsb
< 0 &&
230 diff_pic_order_cnt_lsb
<= -max_pic_order_cnt_lsb
/ 2) {
231 pic_order_cnt_msb
+= max_pic_order_cnt_lsb
;
232 } else if (diff_pic_order_cnt_lsb
> 0 &&
233 diff_pic_order_cnt_lsb
> max_pic_order_cnt_lsb
/ 2) {
234 pic_order_cnt_msb
-= max_pic_order_cnt_lsb
;
236 access_unit
.poc
= pic_order_cnt_msb
+ shdr
.pic_order_cnt_lsb
;
237 prev_pic_order_cnt_lsb
= shdr
.pic_order_cnt_lsb
;
245 // Emit the last access unit.
246 if (access_unit
.has_vcl
) {
247 access_unit
.size
= data_size
- access_unit
.offset
;
248 access_unit_list
.push_back(access_unit
);
251 // Create the list of buffers.
252 // Totally arbitrary decision: assume a delta POC of 1 is 20ms (50Hz field
254 base::TimeDelta poc_duration
= base::TimeDelta::FromMilliseconds(20);
255 for (std::list
<H264AccessUnit
>::iterator it
= access_unit_list
.begin();
256 it
!= access_unit_list
.end(); ++it
) {
257 scoped_refptr
< ::media::DecoderBuffer
> buffer(
258 ::media::DecoderBuffer::CopyFrom(&data
[it
->offset
], it
->size
));
259 buffer
->set_timestamp(it
->poc
* poc_duration
);
260 video_frames
.push_back(
261 scoped_refptr
<DecoderBufferBase
>(new DecoderBufferAdapter(buffer
)));
267 void OnEncryptedMediaInitData(::media::EmeInitDataType init_data_type
,
268 const std::vector
<uint8
>& init_data
) {
269 LOG(FATAL
) << "Unexpected test failure: file is encrypted.";
272 void OnNewBuffer(BufferList
* buffer_list
,
273 const base::Closure
& finished_cb
,
274 ::media::DemuxerStream::Status status
,
275 const scoped_refptr
< ::media::DecoderBuffer
>& buffer
) {
276 CHECK_EQ(status
, ::media::DemuxerStream::kOk
);
279 buffer_list
->push_back(new DecoderBufferAdapter(buffer
));
283 class FakeDemuxerHost
: public ::media::DemuxerHost
{
285 // DemuxerHost implementation.
286 void AddBufferedTimeRange(base::TimeDelta start
,
287 base::TimeDelta end
) override
{}
288 void SetDuration(base::TimeDelta duration
) override
{}
289 void OnDemuxerError(::media::PipelineStatus error
) override
{
290 LOG(FATAL
) << "OnDemuxerError: " << error
;
292 void AddTextStream(::media::DemuxerStream
* text_stream
,
293 const ::media::TextTrackConfig
& config
) override
{
295 void RemoveTextStream(::media::DemuxerStream
* text_stream
) override
{
299 DemuxResult::DemuxResult() {
302 DemuxResult::~DemuxResult() {
305 DemuxResult
FFmpegDemuxForTest(const base::FilePath
& filepath
,
307 FakeDemuxerHost fake_demuxer_host
;
308 ::media::FileDataSource data_source
;
309 CHECK(data_source
.Initialize(filepath
));
311 ::media::FFmpegDemuxer
demuxer(
312 base::ThreadTaskRunnerHandle::Get(), &data_source
,
313 base::Bind(&OnEncryptedMediaInitData
), new ::media::MediaLog());
314 ::media::WaitableMessageLoopEvent init_event
;
315 demuxer
.Initialize(&fake_demuxer_host
,
316 init_event
.GetPipelineStatusCB(),
318 init_event
.RunAndWaitForStatus(::media::PIPELINE_OK
);
320 ::media::DemuxerStream
* stream
= demuxer
.GetStream(
321 audio
? ::media::DemuxerStream::AUDIO
: ::media::DemuxerStream::VIDEO
);
324 DemuxResult demux_result
;
326 demux_result
.audio_config
= stream
->audio_decoder_config();
328 demux_result
.video_config
= stream
->video_decoder_config();
331 bool end_of_stream
= false;
332 while (!end_of_stream
) {
333 base::RunLoop run_loop
;
334 stream
->Read(base::Bind(&OnNewBuffer
,
335 base::Unretained(&demux_result
.frames
),
336 run_loop
.QuitClosure()));
338 CHECK(!demux_result
.frames
.empty());
339 end_of_stream
= demux_result
.frames
.back()->end_of_stream();
347 } // namespace chromecast